Reputation: 7386
Use React for ephemeral state that doesn’t matter to the app globally and doesn’t mutate in complex ways. For example, a toggle in some UI element, a form input state. Use Redux for state that matters globally or is mutated in complex ways. For example, cached users, or a post draft.
My redux state is only representative of what has been saved to my backend database.
For my use case there is no need for any other part of the application to know about a record in an adding/editing state..
However, all my communication with my API is done through redux-thunks. I have found, getting data from redux into local state for editing is tricky.
The pattern I was trying to use:
const Container = () => {
// use redux thunk to fetch from API
useEffect(() => {
dispatch(fetchThing(id));
}, [dispatch, id]);
// get from redux store
const reduxThing = useSelector(getThing);
const save = thing => {
dispatch(saveThing(thing));
};
return (
{!fetching &&
<ThingForm
defaults={reduxThing}
submit={save}
/>}
);
};
const ThingForm = ({defaults, submit}) => {
const [values, setValues] = useState({ propA: '', propB: '', ...defaults});
const handleChange = { /*standard handleChange code here*/ };
return (
<form onSubmit={() => submit(values)}>
<input type="text" name="propA" value={values.propA} onChange={handleChange} />
<input type="text" name="propB" value={values.propB} onChange={handleChange} />
</form>
);
};
How I understand it, ThingForm is unmounted/mounted based upon "fetching." However, it is a race condition whether or not the defaults get populated. Sometimes they do, sometimes they don't.
So obviously this isn't a great pattern.
Is there an established pattern for moving data into local state from redux for editing in a form?
Or should I just put my form data into redux? (I don't know if this would be any easier).
EDIT: I think this is essentially what I am fighting: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html But no recommendation really clearly fits. I am strictly using hooks. I could overwrite with useEffect on prop change, but seems kind of messy.
Upvotes: 2
Views: 1114
Reputation: 1032
EDIT:
const Container = () => {
// use redux thunk to fetch from API
useEffect(() => {
dispatch(fetchThing(id));
}, [dispatch, id]);
// get from redux store
const reduxThing = useSelector(getThing);
const save = thing => {
dispatch(saveThing(thing));
};
return (
{!fetching &&
<ThingForm
defaults={reduxThing}
submit={save}
/>}
);
};
const ThingForm = ({defaults, submit}) => {
const [values, setValues] = useState({ propA: '', propB: '', ...defaults});
const handleChange = { /*standard handleChange code here*/ };
useEffect(() => {
setValues({...values, ...defaults})
}, [defaults]);
const submitValues = (e) => {
e.preventDefault();
submit(values)
}
return (
<form onSubmit={submitValues}>
<input type="text" name="propA" value={values.propA} onChange={handleChange} />
<input type="text" name="propB" value={values.propB} onChange={handleChange} />
</form>
);
};
What you are doing is the right way, there's no reason why you should put the form data in the redux store. Like you said, "there is no need for any other part of the application to know about a record in an adding/editing state" And that's correct.
The only problem you have is here:
{!fetching &&
<ThingForm
defaults={reduxThing}
submit={save}
/>}
Assuming fetching
is true on every dispatch:
Instead of trying to hide the component (unmounting essentially), you should maybe use a spinner that overlays the page?
I don't know the rest of your code to comment on a better approach.
You also don't have to add dispatch to the dependency array
useEffect(() => {
dispatch(fetchThing(id));
}, [id]);
From the react docs:
React guarantees that dispatch function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.
Upvotes: 2