简体   繁体   中英

React does not re-render when state changes

I have a list of warehouses that I pull from an API call. I then render a list of components that render checkboxes for each warehouse. I keep the state of the checkbox in an object (using the useState hook). when I check/uncheck the checkbox, I update the object accordingly.

My task is to display a message above the checkbox when it is unchecked. I tried simply using the object, however, the component was not re-rendering when the object changed.

I found a solution to my problem by simply adding another useState hook (boolean value) that serves as a toggle. Since adding it, the component re-renders and my object's value is read and acted on appropriately.

My question is: why did I have to add the toggle to get React to re-render the component? Am I not updating my object in a manner that allows React to see the change in state? Can someone explain to me what is going on here?

I've created a sandbox to demonstrate the issue: https://codesandbox.io/s/intelligent-bhabha-lk61n

function App() {
  const warehouses = [
    {
      warehouseId: "CHI"
    },
    {
      warehouseId: "DAL"
    },
    {
      warehouseId: "MIA"
    }
  ];

  const [warehouseStatus, setWarehouseStatus] = useState({});
  const [toggle, setToggle] = useState(true);

  useEffect(() => {
    if (warehouses.length > 0) {
      const warehouseStates = warehouses.reduce((acc, item) => {
        acc[item.warehouseId] = true;
        return acc;
      }, {});
      setWarehouseStatus(warehouseStates);
    }
  }, [warehouses.length]);

  const handleChange = obj => {
    const newState = warehouseStatus;
    const { name, value } = obj;
    newState[name] = value;
    setWarehouseStatus(newState);
    setToggle(!toggle);
  };

  return warehouses.map((wh, idx) => {
    return (
      <div key={idx}>
        {!warehouseStatus[wh.warehouseId] && <span>This is whack</span>}
        <MyCheckbox
          initialState
          id={wh.warehouseId}
          onCheckChanged={handleChange}
          label={wh.warehouseId}
        />
      </div>
    );
  });
}

Thanks in advance.

You are mutating state ( don't mutate state )

this:

  const handleChange = obj => {
    const newState = warehouseStatus;
    const { name, value } = obj;
    newState[name] = value;
    setWarehouseStatus(newState);
  };

should be:

  const handleChange = ({name,value}) => {
    setWarehouseStatus({...warehouseStatus,[name]:value});
  };

See the problem?

    const newState = warehouseStatus; <- this isn't "newState", it's a reference to the existing state
    const { name, value } = obj;
    newState[name] = value; <- and now you've gone and mutated the existing state

You then call setState with the same state reference (directly mutated). React says, "hey, that's the same reference to the state I previously had, I don't need to do anything".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM