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}
...
/>
)
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]);