0

I have a View and Column components. I use registerColumn method to store column refs in the View context:

const [columns, setColumns] = useState([]);

const registerColumn = (node) => {
    const column = {
        someUniqueId: Math.random(),
        node,
        // more props
    };

    setColumns(prev => [...prev, column])
}

const context = {
    columns,
    registerColumn
}

<ViewContext.Provider values={context}>
    <View>
        <Column/>
        <Column/>
        <Column/>
    </View>
    <View>
        <Table/>
    </View>
</ViewContext.Provider>

Where I'm getting really confused is the Column component. Whenever I try to register the column and get the updated length afterwards, it's not being updated on time, because setState works asynchronously:

const {columns, registerColumn} = useContext(ViewContext);
const ref = useRef(null);

useEffect(() => {
    registerColumn(ref.current);

    // Here is where updated column length but it keeps saying it's 0 length
    console.log(`Hello from column number ${columns.length}`);
}, []);

[...]

Docs are saying, that I can make use of the useEffect with the columns as the dependency and have the updated length on next render. However, I need to consume updated state right after registerColumn is invoked - using another useEffect will basically mean breaking execution context and the need to delay the task, plus possibly introduce other callbacks just to get around this.

I'm new to React but I'm pretty sure there are other possibilities to get around this, so any help will be greatly appreciated.

RA.
  • 969
  • 13
  • 36

1 Answers1

-1

Updating setState in useEffect and console log in useEffect won't work, because useState works async and react batches all state and update it once. It is updating the state but it won't reflect that that monent because react batches with updates.
You can use other useEffect with dependency of columns, so every time registerColumn updates, second useEffect will be called and console log the value.

useEffect(() => {
    registerColumn(ref.current);
}, []);


useEffect(() => {
    console.log(`Hello from column number ${columns.length}`);
},[columns]);

Ferin Patel
  • 3,424
  • 2
  • 17
  • 49
  • The thing is, as you can see in the JSX I posted, there are 3 columns in there. When mounted, it all happens too quickly and console message will say the same message 3 times: `Hello from column number 3` ignoring 1 and 2 :( – RA. May 11 '20 at 06:11
  • I see async mentioned many times but problem is not because of asynchronous methods, you are simply logging a [stale closure](https://dmitripavlutin.com/react-hooks-stale-closures/). Setstate being async is in class components. – HMR May 11 '20 at 07:05
  • @HMR I don't believe this is actually true. The fact is `useEffect` has a missing dependency of `columns`, however introducing one doesn't make a change because [useState still works asynchronously](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – RA. May 11 '20 at 21:07