6

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}
        ...
    />
)
Lenny Meerwood
  • 61
  • 2
  • 10

1 Answers1

4

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]);
gdh
  • 13,114
  • 2
  • 16
  • 28
  • Hey thanks for the advice. Unfortunately I did try that too, and it didn't work. Which I find *really* weird. Also, I would gladly pass just the prop I wanted, however, I'm dealing with a library that doesn't let me do that (react-jsonschema-form). – Lenny Meerwood May 21 '20 at 04:37
  • got it... just saw your updated question... i will update the answer now – gdh May 21 '20 at 05:09
  • That's done it! Thanks so much. I'd vote your answer up but I am too noob to give points. Thanks again so much! Edit: Apparently I can now vote. – Lenny Meerwood May 21 '20 at 05:26