I have a react component with a useEffect that is not being fired when the props change. I would like this component to update what it is displaying based on the prop. However, when the prop changes nothing happens.
For debugging I added a button that would manually update the value, that works fine. I just cannot get the useEffect to run for the life of me.
const ReadOnly = (props: WidgetProps) => {
const [value, setValue] = React.useState('No Value');
const { formContext } = props;
const firstName = () => formContext.queryData[0]?.basicInformation?.firstName ?? 'No value';
// This only runs once when the component is first rendered never fired when formContext updates.
React.useEffect(() => {
setValue(firstName());
}, [formContext]);
// This correctly updates the value
const onClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault();
setValue(firstName());
};
return (
<>
<p>{value}</p>
<Button onClick={onClick}>Update Value</Button>
</>
);
};
export default ReadOnly;
I have tried changing the value the useEffect functions watches from formContext
to formContext.queryData[0].basicInformation.firstName
to no effect. I've also tried using props
instead. I even tried removing the watch array altogether in the hopes it would just spam update, but nothing.
Any advice on how I can achieve this? I know the value is getting through to the component because it works when I press the button. And when I check it out in the React inspector on the webpage I can clearly see that the value is there. Cheers.
Edit:
This component is being used as a widget for react-jsonschema-form. Basically what happens is everytime the formData
is updated I pass it back into the formContext
. formContext
is the only way I can get other data to that widget. Below is a stripped down version of what I am doing.
const [formDataAndIncludings, setFormDataAndIncludings] = React.useState<any>(null);
ClientRect.useEffect(() => {
...
// Initial loading of formDataAndIncludings
...
}, [])
const onChange = (e: IChangeEvent<any>) => {
const newFormDataAndIncludings = { ...formDataAndIncludings };
newFormDataAndIncludings.queryData[0] = e.formData;
setFormDataAndIncludings(newFormDataAndIncludings);
};
return (
<Form
...
onChange={onChange}
formContext={formDataAndIncludings}
...
/>
)
Just like the state, we can also use props as a dependency to run the side effect. In this case, the side effect will run every time there is a change to the props passed as a dependency. useEffect(() => { // Side Effect }, [props]);
Run useEffect When a Prop Changes Just as we were able to set up useEffect to run when a state variable changed, the same can be done with props. Remember they're all regular variables! useEffect can trigger on any of them.
Does useEffect run after every render? Yes! By default, it runs both after the first render and after every update.
Use the useEffect hook to listen for state changes in React. You can add the state variables you want to track to the hook's dependencies array and the logic in your useEffect hook will run every time the state variables change.
The state is being mutated directly in the parent's onChange
and hence your useEffect in ReadOnly
component is not being triggered.
Update your onChange
const onChange = (e: IChangeEvent<any>) => {
setFormDataAndIncludings(prev => ({
...prev ,
queryData : [
e.formData,
...prev.queryData.slice(0,1)
]
}))
//const newFormDataAndIncludings = { ...formDataAndIncludings };
//newFormDataAndIncludings.queryData[0] = e.formData;
//setFormDataAndIncludings(newFormDataAndIncludings);
};
Provide useEffect dependency as usual
React.useEffect(() => {
setValue(firstName());
}, [formContext]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With