In my React/ Redux app, I have a text input with its default value retrieved via Ajax call. User can edit the default value in the input, and then submit the updated value by clicking on a 'submit' link. I'm struggling with using either uncontrolled or controlled inputs for this.
Using uncontrolled input with defaultValue: the component doesn't get re-rendered when the data for default value comes back from initial Ajax call (detailed in official document here). So the input field is blank all the time.
Using controlled input with value bound to component's props: This does give the default value correctly, however since I can't change the props, the field is basically "locked". I can get around this by triggering an action to modify the global state in onChange handler and force the whole component re-rendering, but this again poses other issues. For one it seems excessive to do so in onChange, and also I don't want to commit to changing the state before user clicking the submit link.
Any suggestions what I can do here?
As the docs say, the defaultValue is only useful at the initial render. So if you want to use defaultValue you have to delay the rendering of that particular component until after the data is loaded. Consider putting a loading gif (or something similar) in place of the form for the AJAX call.
I don't think the second way - using value and updating with onChange - is as bad as you say; it's generally how I write my forms. However, the real problem here is once again the delayed loading. By the time the initial value loads in, a user may already have filled in that input, only to see it overwritten with the received AJAX value. Not fun.
The best way in my view is simply to not use AJAX. Append your initial data to the webpage itself as a global variable. This may sound like an anti-pattern but you only ever read the global once, and it saves you cost of an AJAX request. In Redux there's a convenient way of doing this which I've documented here and here.
Just to add to David's answer.
Sometimes it does make sense to make an AJAX call to load defaults.
For ex. you could be calling some service for multi language support, creating an element in the data base with default values, etc.
I would go with the value-onChange approach and prevent the user from editing the value before it is loaded:
By disabling the input until the AJAX call returns. Just bind the disabled property to some prop that shows the default was loaded.
By not rendering the input until the AJAX call returns. When you get the default value from the server you modify the state so it triggers a re render.
Related
[This is the Codesandbox with all of the code that exemplifies the issue I'm having. Sorry, SO doesn't allow for proper React code with TSX, etc.]
I have some tabs, naturally, whenever I click on each of them, my state changes (namely selectedTab) and the body that's being rendered. Each tab item has a body that goes with it, the first tab, in my case, has a component that, if you click, a count is increased. Well, whenever the tab changes, that count gets reset. In fact, it's a full-rerender all around.
The problem with doing a full re-render is that - not only is it inefficient, given how the user's gonna use it, but it also adds fuel to the fire: what if the user inputs something on a tab, then switches to another tab to do something else and then back to the original one? He's just lost what he typed in.
Naturally, this can be solved if we render all of the tabs' content upfront and we just play with display: none, and it'd be a good solution, but I'm also trying to learn.
I understand how React re-renders things and why this happens, but is there a way to fix it?
In my case, if you look at useTabs, the content variable simply looks at the corresponding tab's component key and takes outputs that. It's there that, I think, I need to memoize things.
Uhm, I guess you can't prevent calling your custom hook, this will lead to invariant violation error.
These hooks in the component will be executed on each render -> thus 'resets' your tabs.
You should rely on useEffect dependencies argument to run conditional code to see if values needs to be changed.
as long as your in the same app you could use contextApi,react context docs otherwise you can use localstorage, localstorage tutorial
or just create this state in the parent component and pass it down as props?
This is a bit complicated so I'll try to put it as simple as possible.
I have a pretty much complicated reusable component in my app.
Basically, it is a large table with many editing options.
Since this component should be reusable, my thought was that it should manage it's own state.
It also makes sense because this component's business logic is pretty much complicated, and by using redux, I'll probably have to repeat some crazy boilerplates.
On the other hand, this component should be able to have some default data loaded to, and finally, this component is in some cases a part of a form, so I sohuld be able to extract it's data and send it to the server.
Redux can really help with the last two tasks - if I had an easy way to store all component changes in the store, I could easily load the default data from there (because my component will be fully controlled), and it will also be easy to extract data from the store when sending to server.
Although, it has a lot of boilerplate, and I'm not feelinng comforotable to write a componenet specific logic in my reducers, since ideally, they could manage themselves.
Any more ideas about that?
I had one idea that seems to be working, though I am not sure how good it is:
Have a "dataKey" prop to handle default data prop changes, and derive the state from the data
Use some submit callback to extract the data and send to server
Any more thoughts will be very helpful, thakns!
It's hard to provide an extract answer as your question is kind of abstract. But since you are just looking for ideas, this is just an idea which you can try to incorporate with the actual use case.
In this kind of scenario, I would first distinguish my actual data and UI state. Then I will write my component as a controlled component of actual data with the usual value and onChange props. The UI state will be kept in the internal state of the component and it will either be derived from the initial props or initialized with a default value. When the user interacts with the component, if the change affects only the internal state, we can just use setState to update the state. If it affects data also, we can fire onChange prop also accordingly. This way we can keep the actual data in the redux store, but still, keep component specific logic in the component.
As an example, let's say we want to make a text label component with in-line/in-place editing support. I would still keep the props of this component similar to the default HTML input element as value and onChange. But the flag that indicates whether to render a text or input element with 'Submit' and 'Cancel' buttons will be kept in the component as isEditMode. Initially, we can always keep isEditMode as false. But when the user clicks on the text, we change it to true, so that component will render elements for editing and hide the label. While the user changes the text we can keep that intermediate value also in UI state. But when the user clicks the 'Submit', we can fire onChange with the new value in state and change isEditMode also to false. Likewise, we can keep the component as a control component but still use the component state to manage intermediate UI states.
Hope this helps!
Using redux-form here. Part of the form is inited asyncronously after an ajax request (a list of items in a select) that depends on what the user selected on a separate field.
To achieve this I put the new items in the initialValues and I enableReinitialize: true. The select pre-fills properly and all is good.
But since I want to retain the other values that the user might have added in the form, I also keepDirtyOnReinitialize: true.
So far so good.
The problem is that if the user navigates back to another page (without submitting the form) I would like to clear the all form and start from scratch.
It seems that the form is correctly destroyed on unmount but when we navigate back to the form again, the previous values are retained because of the keepDirty...
Should the unmounting of the form beat the keepDirty? Otherwise is it the only option to manually cleanup the form before navigating back to the other page or am I missing a simpler way?
Problem was the 'dirty' values are actually taken from redux store so it is not the form retaining them but it is me retaining them and feeding them again to redux-form in the initial values. I need to rethink a bit my code.
I have a page in my react/redux app that has a search field and I can make an ajax request based on input and display the result on the screen. The problem is that whenever I leave the page and return back it preserves the data I fetched. What is a conventional way of resetting a component whenever you leave the page? I should probably do something with componentWillUnmount function, but things don't quite work.
You store the state in a redux store. Thus the behavior - the data is still there in the store when you navigate between pages. It needs to be removed upon navigating away from page. This could be done by dispatching a reset action from the componentWillUnmount lifecycle method of the page component.
Do you need to be storing the fetched data in your redux store? If you are looking for the data to be loaded every time you go to the page and reset whenever you leave, just storing the data in component state would probably be a better fit, since it gives you that behavior by default.
I am new on react. I am working on react application with redux. I have a form (I am using redux-form) by which I can save data or edit data.
my problem is , In edit mode I populate data using componentWillReceiveProps. and populated perfectly, now when I try to clear any field on form its again fill.
componentWillReceiveProps(nextProps){
this.props.dispatch(initialize('NewProject', nextProps.project.project[0]));
}
I would be grateful for any help.
Is there a reason you're not dispatching this action somewhere else, like in componentDidMount? I can't say without seeing more code, but it's possible that whenever you edit your form, React again calls componentWillReceiveProps and overwrites whatever you did with the behavior you've given your component.
Per the React documentation:
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
It may be a good idea for you to move your dispatch to a more predictable event if possible, like componentDidMount. Typically, your render method should be capable of handling different cases if a component has multiple possible states. You could also create an edit and save version of your component, and render one or the other based on the props you receive. The best way to "populate" data, as you put it, is to define propTypes for your component, and then use your props to insert that data into elements in the render method.