Search code examples
reactjscomponentsstate

State use as props in component, state change will not re-render the component


React will re-render if the state is changed. Then why the following situation cannot be re-render?

Base on the program behavior, I found that

  1. when state change, if the state is use directly in the component, it will be re-rendered.
  2. when state change, if the state is use as props in child component, it will not be re-rendered.
  3. how can I directly re-render that child component?

The following program is come from my code with some modification. Update: SandBox This is the code i created in sandbox, I have try to simulate my real situation

function List(props) {

    const [fullListValue, setFullListValue] = useState([]); //store data which get from server
    const [dropDrowList, setDropDrowList] = useState([]); //store data column value (e.g. data contain "fruit" column, this state will store "apple", "berry", "banana" etc.)

    const setAllList = (data) => {
        setFullListValue(data);
    };
    useEffect ( ()=> {
      //axios call server, use setAllList(data) to save state
    ,[]}

    //when fullListValue is updated, 
    useEffect(() => {
        const dropDrowListCopy = [{ ['label']: 'ALL', ['value']: 'ALL' }];
        //push all "fruit" data in dropDrowListCopy without duplicate
        console.log(dropDrowListCopy); //this will show all dropdown I want, i.e. "ALL","apple", "berry", "banana"
        setDropDrowList(dropDrowListCopy); //set to dropDrowList, and I expect this step will re-render the SelectionList component, however, it do not
    }, [fullListValue]);

    return (
        <div>
            <div>
                {dropDrowList.length <= 1 ? (
                    'loading'  //I add this to re-render the component, but I think this is not a good way to re-render the component
                ) : ( 
                    <SelectionList
                        itemList={dropDrowList}
                    />
                )}
            </div>
        </div>
    );
}

Solution

  • Issue

    In SecitonList, you are storing passed props into state but not updating state when props change, leaving you with stale state. It's actually a React anti-pattern to store props in local component state for this reason.

    Solution

    Update state when props change.

    function SelectionList(props) {
      const [listItem, setListItem] = useState(
        props.itemList ? props.itemList : []
      );
    
      useEffect(() => {
        setListItem(props.itemList);
      },
      [props.itemList]);
    
      return (
        <FormControl>
          <Select
            renderValue={
              props.multiple ? (selected) => fieldDisplay(selected) : null
            }
            input={<Input />}
          >
            {listItem.map((item) => (
              <MenuItem key={item.value} value={item}>
                {props.multiple ? (
                  <Checkbox checked={selectedValue.indexOf(item) > -1} />
                ) : null}
                <ListItemText primary={item.label} />
              </MenuItem>
            ))}
          </Select>
        </FormControl>
      );
    }
    

    Solution - directly consume props

    function SelectionList(props) {
      return (
        <FormControl>
          <Select
            renderValue={
              props.multiple ? (selected) => fieldDisplay(selected) : null
            }
            input={<Input />}
          >
            {props.itemList.map((item) => (
              <MenuItem key={item.value} value={item}>
                {props.multiple ? (
                  <Checkbox checked={selectedValue.indexOf(item) > -1} />
                ) : null}
                <ListItemText primary={item.label} />
              </MenuItem>
            ))}
          </Select>
        </FormControl>
      );
    }