Search code examples
reactjsreact-hooks

React context useEffect not triggered when dependency is called from within another component


In a component i use a context and its state functions and then call the setReviewConfig

const { reviewConfig, setReviewConfig } = useContext(ReviewConfigContext);
// other code
console.log('Running removeYelpConfig ... modified reviewConfig')
        console.log(reviewConfig)
        setReviewConfig(reviewConfig) // this is in the other component where i call the setter

The console log is running so i know what is being passed to setReviewConfig and the reviewConfig is indeed modified.

In the context I have ...

const [reviewConfig,setReviewConfig] = useState(null)

....
useEffect(() => {
            if( reviewConfig !== null){
                console.log("ReviewConfigContext reviewConfig, ")
                console.log(reviewConfig)
...
            }else{
                console.log("ReviewConfigContext not updating reviewConfig ")
            }

    }, [reviewConfig, setReviewConfig])

But when the component modifies reviewConfig or calls that function setReviewConfig the useEffect in the context is not run, i know because the console.log in the useEffect are not run.

I have deployed an extract of the app on this replit https://replit.com/@bizmate/SetterComponentCall#src/components/YelpConfigDisplay.jsx it runs also from https://c6c0f2a9-1f10-43c0-8c9a-06ae0dc27fd4-00-1bpt8cb4rltiq.janeway.replit.dev/ while i am in the workspace (most likely you will not be able to run it unless you fork it or I am running it). Just to confirm, the modified reviewConfig is indeed modified correctly in the component as per console.log but the useEffect of the context is not run after setReviewConfig is being called.

What am I missing? Is any of the above bad practice? My ultimate goal is to do any further networking or state change related operation on reviewConfig in the context so that any component can call the setter or modify the variable but does not need to implement network calls etc, as these will be handled by the context


Solution

  • reviewConfig.sourceConfigs.splice(
      reviewConfig.sourceConfigs.findIndex((el) => el.sourceType.name == "Yelp", 1),
    );
    console.log("Running removeYelpConfig ... modified reviewConfig");
    console.log(reviewConfig);
    setReviewConfig(reviewConfig);
    

    One of the core assumptions that react makes is that state is immutable. When you set state, react compares the old state and the new state using Object.is (similar to ===). Since it's the same object before and after, it looks to react like it has not changed. Thus the render gets skipped and the effect does not run.

    You must create a new state instead of mutating the old one.

    setReviewConfig(prev => {
      const next = {
        ...prev, // Copy the old object into a new one
        sourceConfigs: prev.sourceConfigs.filter(el => el.sourceType.name != "yelp")
      }
      return next;
    });