useState doesn't update state passed in component

538 Views Asked by At

The SelectedColumn value doesn't come in the CustomHeader component. However, setSelectedColumn works! Why ? Also, I'm passing CustomHeader to constant components that use useMemo. Without useMemo CustomHeader doesn't work.

  const [selectedColumn, setSelectedColumn] = useState(null);
  console.log("selected Column Outside:", selectedColumn); // It works!

  const CustomHeader = (props) => {
    const colId = props.column.colId;

    console.log("selected Column In CustomHeader:", selectedColumn); // Doesn't work
    return (
      <div>
        <div style={{float: "left",  margin: "0 0 0 3px"}} onClick={() => setSelectedColumn(props.column.colId)}>{props.displayName}</div>
        { selectedColumn === colId ? <FontAwesomeIcon icon={faPlus} /> : null}
      </div>
    )
  }

  const components = useMemo(() => {
    return {
      agColumnHeader: CustomHeader
    }
  }, []);

UPDATE: If I use the useState hook inside the CustomHeader component, it adds a "+" sign to each column and does not remove from the previous one. Here is a picture:

enter image description here

2

There are 2 best solutions below

0
Gabriel d'Agosto On BEST ANSWER

After reading your comment, your issue is clearly about where you want to place your useState.

First of all, you should always place useState inside a component. But in your case, apparently what you're trying to achieve is that when you select a column, the other columns get deselected.

Therefore, you need to pass both selectedColumn and setSelectedColumn as props to your component, and create the useState on the parent component.

Assuming all your CustomHeader components share the same parent component, in which my example I'll call CustomHeadersParent, you should do something like this:

// added mock headers to have a working example
const headers = [
  {
    displayName: "Game Name",
    column: {
      colId: 1,
    },
  },
  {
    displayName: "School",
    column: {
      colId: 2,
    },
  },
];

const CustomHeadersParent = (props) => {
  const [selectedColumn, setSelectedColumn] = useState(null);

  return headers.map((h) => (
    <CustomHeader
      column={h.column}
      displayName={h.displayName}
      setSelectedColumn={setSelectedColumn}
      selectedColumn={selectedColumn}
    />
  ));
};


const CustomHeader = (props) => {
  const colId = props.column.colId;

  return (
    <div>
      <div
        style={{ float: "left", margin: "0 0 0 3px" }}
        onClick={() => props.setSelectedColumn(props.column.colId)}
      >
        {props.displayName}
      </div>
      {props.selectedColumn === colId ? <FontAwesomeIcon icon={faPlus} /> : null}
    </div>
  );
};

const components = useMemo(() => {
  return {
    agColumnHeader: CustomHeader,
  };
}, []);

1
vairaden On

You should use hooks inside your component

  const CustomHeader = (props) => {
    const colId = props.column.colId;

    const [selectedColumn, setSelectedColumn] = useState(null); 

    console.log("selected Column In CustomHeader:", selectedColumn); // Should work
    return (
      <div>
        <div style={{float: "left",  margin: "0 0 0 3px"}} onClick={() => setSelectedColumn(props.column.colId)}>{props.displayName}</div>
        { selectedColumn === colId ? <FontAwesomeIcon icon={faPlus} /> : null}
      </div>
    )
  }