How the React Context API detects changes and re-renders consumers?

I need to understand that how the consumers of a particular provider knows that the value has been changed and the consumers need re-rendering? The documentation says that the algorithm detects the value changes using Object.is() and I do not quite understand why we ever need to detect changes in the consumers, since the ONLY way we can change the context value is updating the state in the parent component, which always triggers re-renders in every child components of the parent component and we always get fresh context data.

Or the consumers check for changed values because is there any mechanism to provide the changed value to the consumers without using the state in the parent component at all?

class App extends Component {
  state = {
    userState: {
      userName: "yasmikash",
      userId: "u8662",
      signedIn: true,
    },
  };

  handleOnChange = () => {
    this.setState({
      userState: {
        ...this.state.userState,
        signedIn: !this.state.userState.signedIn,
      },
    });
  };

  render() {

    return (
      <>
        <h1>App</h1>
        <button onClick={this.handleOnChange}>Toggle Signed In</button>

        <AuthContext.Provider value={this.state.userState}>
          {/* <Component1 > */}
          {/* <Component2 > */}
          <Profile />
          {/* </Component2> */}
          {/* </Component1 > */}
        </AuthContext.Provider>
      </>
    );
  }
}

Essentially the same as if you pass new props to a component or if you update the state of a component. It compares the previous and the new props, if they’re the same then it can avoid rerendering. Object.is is a JS built-in function to check if two objects are the same: if the two objects are not the same, rerender (rerun the function for a given component). Note this is why it is stressed in React that you should not mutate values: if you mutate objects instead of replacing them, then they will always be the same object, so no rerenders will occur.

That is how you get fresh context data, if there wasn’t a way to detect changes then it wouldn’t make any difference how many times you updated the value in the provider, nothing would happen.

No, because the only point of context is to be able to use the object stored in the provider, ie the state, further down the component tree. It’s the same as having a parent component that passes state to a child via props, it just lets you have any number of child components in-between the parent and the place/s you want to use it.

2 Likes

Let’s say for ex a parent component renders a child component, and it also passes down some context value to the child component. The context data passed down is simply an object reference stored in the parent component, but the child component re-renders every time when the state of the parent component changes, but the context data remains the same. If we consider about this scenario, React does the comparison using Object.is when the child tries to re-render itself, since the reference is the same, as I understand, a re-render for this child component is not necessary. But it will re-render anyways cause it is how React works, unless if we don’t provide shouldComponentUpdate method. What I still don’t get is why it’s needed doing the comparison to begin with when React gets fresh context data every time.

You seem to be describing a situation where a component passes both context and props to a child. And then some state in the component changes, and new props are passed to a child, but the data in context is the same. In this extremely contrived scenario, a rerender is going to happen, because the props change – it’s just how react works, if you pass new props to a component it will rerender.

What I still don’t get is why it’s needed doing the comparison to begin with when React gets fresh context data every time.

Assuming you don’t have the situation you’re describing, where it’s irrelevant because the component will rerender regardless: it’s a basic sanity check. There are other component functions being run all the time, every time something in the app changes. You need something that says “I don’t need to rerun, nothing has changed”. If the object stored in the context is exactly the same as it was last time it got checked, then there’s no need to do anything.

1 Like