reactjsreact-hooksuse-context

Don't update children use react useContext


I have two children Components, when I onChange in first children, then the second children re render, I don't want to the second children re render. Online code example: https://codesandbox.io/s/billowing-glitter-r5gnh3?file=/src/App.js:1287-1297

const EditSceneModalStore = React.createContext(undefined);

const Parent = () => {
  const [saveValue, setSaveValue] = React.useState({});
  const initValue = {
    name: "zhang",
    age: 3
  };

  const onSave = () => {
    console.log("===saveValue==", saveValue);
  };

  const onChangeValue = (key, value) => {
    const newValue = {
      ...saveValue,
      [key]: value
    };
    setSaveValue(newValue);
  };
  return (
    <EditSceneModalStore.Provider
      value={{
        initValue,
        onChangeValue
      }}
    >
        <ChildInput1 />
        <ChildInput2 />
        <Button onClick={onSave} type="primary">
          save
        </Button>
    </EditSceneModalStore.Provider>
  );
};

const ChildInput1 = () => {
  const { onChangeValue, initValue } = React.useContext(EditSceneModalStore);
  const [value, setValue] = React.useState(initValue.name);
  return (
    <Input
      value={value}
      onChange={(v) => {
        setValue(v.target.value);
        onChangeValue("name", v.target.value);
      }}
    />
  );
};

const ChildInput2 = () => {
  const { initValue, onChangeValue } = React.useContext(EditSceneModalStore);
  const [value, setValue] = React.useState(initValue.InputNumber);
  console.log("====ChildInput2===");
  return (
    <InputNumber
      value={value}
      onChange={(v) => {
        setValue(v.target.value);
        onChangeValue("age", v.target.value);
      }}
    />
  );
};

when I onChange in ChildInput1, then ChildInput2 re-render, I don't want to the ChildInput2 re-render. Example image

enter image description here


Solution

  • As Andrey explained, you should fix the following line:

    //you have
    const [value, setValue] = React.useState(initValue.InputNumber);
    // should be
    const [value, setValue] = React.useState(initValue.age);
    

    Additionally, initValue gets unnecessarily re-computed on every re-render, so it should be outside the scope of Parent:

    const initValue = {
        name: "zhang",
        age: 3
      };
    
    const Parent = () => {...}
    

    Regarding re renderings, it is ok. When a Provider gets the value changed, all their childs wrapped in a consumer rerender. This is natural. This post explains why.

    A component calling useContext will always re-render when the context value changes. If re-rendering the component is expensive, you can optimize it by using memoization.

    In this case, it is not expensive enough to consider memoization.

    I Hope it helps