1

I have the main app with two subcomponents, both receiving the same model property.

The first subcomponent handles a number prop from model.myNumberProperty and the second subcomponent handles an array prop from model.myArrayProperty

Changing the model.myNumberProperty in subcomponent one triggers a re-render but changing the model.myArrayProperty in subcomponent two doesn't

function App() {
  const model = MyModel();

  return (
    <div className="App">
      Main app
      <ComponentOne dataObject={model} />
      <ComponentTwo dataObject={model} />
    </div>
  );
}

export default function MyModel() {
  let _propOne = 0;
  let _propTwo = ["one"];

  const propOne = () => {
    return _propOne;
  };
  const propTwo = () => {
    return _propTwo;
  };

  const modifyPropOne = () => {
    _propOne++;
    console.log("modifying prop one", _propOne);
  };

  const modifyPropTwo = () => {
    _propTwo.push("new element");
  };

  return Object.freeze({ propOne, propTwo, modifyPropOne, modifyPropTwo });
}


const ComponentOne = props => {
  const [propOne, setPropOne] = useState(props.dataObject.propOne());

  const onButtonOneClick = () => {
    props.dataObject.modifyPropOne();
    setPropOne(props.dataObject.propOne());
  };

  return (
    <div>
      <p>Hello ComponentOne:</p>

      <button onClick={onButtonOneClick}>ModifyPropOne</button>
      <p>{propOne}</p>
    </div>
  );
};

const ComponentTwo = props => {
  const [propTwo, setPropTwo] = useState(props.dataObject.propTwo());

  const onButtonTwoClick = () => {
    props.dataObject.modifyPropTwo();
    setPropTwo([...props.dataObject.propTwo()]);
    console.log('Prop two', props.dataObject.propTwo());
  };
  return (
    <div>
      <p>Hello ComponentTwo</p>

      <button onClick={onButtonTwoClick}>ModifyPropTwo</button>
      {propTwo.map((value, index) => (
        <p key={index}>{value}</p>
      ))}
    </div>
  );
};

Please look at this CodeSandbox to test the case.

I am using React Hooks. The solution to my problem should be React only, so no Redux or other 3rd party libs.

Please note that my model is a freezed object to enforce encapsulation so I can not just recreate the whole object. The model in the example is a reduced and simplified one, In my real project, it is a complex Card game object which handles lots of arrays.

1
  • 1
    Next time please add all necessary code in the question in addition to the sandbox, cheers. Commented Oct 27, 2019 at 5:17

1 Answer 1

2

On calling setState, React makes shallow comparison with the previous state and decides if the render phase should proceed.

In your case, it has the same reference, therefore no render triggered.

To fix it, set a copy of your array as the new state.

const onButtonTwoClick = () => {
  props.dataObject.modifyPropTwo();
  //                v Make a new copy.
  setPropTwo([...props.dataObject.propTwo()]);
};

Refer to The power of not mutating Data.

Edit updateArrayProp

Sign up to request clarification or add additional context in comments.

1 Comment

life can be simple :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.