在需要状态更改的代码运行后,进行状态更新

时间:2020-01-05 22:36:50

标签: reactjs react-hooks

我正在尝试创建一个节点网格,在单击/拖动它们时会更新。我遇到了一个怪异的React State,在该状态下的代码运行后,该状态正在更新。

预期行为:

  • nodeTypePointer设置为0作为默认反应状态
  • 单击空白节点,将nodeTypePointer设置为0(未更改)+逻辑使用0
  • 单击有色节点,将nodeTypePointer设置为1 +逻辑使用1

实际行为

  • nodeTypePointer设置为0作为默认反应状态
  • 单击空白节点,nodeTypePointer保持不变+逻辑使用0
  • 第一次单击有色节点 ,将nodeTypePointer设置为0(未更改)+逻辑使用0,然后在代码完成后将其设置为1 / li>
  • 第二次单击彩色节点 ,现在nodeTypePointer仍设置为1,逻辑使用1,然后在代码完成后 nodeTypePointer设置为0

This can be seen on this screenshot

我知道状态是异步的,但是不确定继续进行的确切原因/问题/解决方案。如果我要使用UseEffect,则不确定如何将参数传递给下一个函数。

代码如下:

import React, { useEffect, useState } from 'react';
import Node from './Node'

import './Grid.css';

const rectDiameter = 25
const gridHeightRatio = 0.9

const Grid: React.FC = () => {

    const gridHeight: number = Math.floor(window.innerHeight * gridHeightRatio)
    const gridWidth: number = window.innerWidth

    const numX: number = Math.floor(gridHeight / rectDiameter)
    const numY: number = Math.floor(gridWidth / rectDiameter)

    const tempGrid: number[][] = [...Array(numX)].map(() => Array(numY).fill(0));

    const [grid, setGrid] = useState(tempGrid)
    const [isMousePressed, setMousePressed] = useState(false)
    const [nodeTypePointer, setNodeTypePointer] = useState(0)

    useEffect(() => {
        console.log('Use Effect', nodeTypePointer)
    }, [nodeTypePointer])

    // Hacky el.ID to workaround React performance until I know how to do it better
    const paintNode = (x: number, y: number) => {
        const el = document.getElementById(`${x}-${y}`)
        if (!el) return
        if (nodeTypePointer === 0) {
            el.classList.add("node-wall")
        } else {
            el.classList.remove("node-wall")
        }
    }

    const updateGridPosition = (x: number, y: number) => {
        const newValue = nodeTypePointer === 0 ? 1 : 0
        let newGrid: number[][] = grid
        newGrid[x][y] = newValue
        setGrid(newGrid)
    }

    const handleMouseDown = (event: React.MouseEvent<HTMLDivElement>, x: number, y: number) => {
        console.log('Node pointer type Before', nodeTypePointer)
        setNodeTypePointer(grid[x][y])
        console.log('Node pointer type After', nodeTypePointer)
        setMousePressed(true)
        console.log('Before updated Grid', grid[x][y])
        updateGridPosition(x, y)
        console.log('After updated Grid', grid[x][y])
        paintNode(x, y)                             // <------------- Uses nodeTypePointer
    }

    const handleMouseUp = (event: React.MouseEvent<HTMLDivElement>, x: number, y: number) => {}

    const handleHover = (event: React.MouseEvent<HTMLDivElement>, x: number, y: number) => {
        if (!isMousePressed) return
        updateGridPosition(x, y)
        paintNode(x, y)
}

    return (
        <div className='gridContainer' >
            {
                grid.map((row: number[], i: number) =>
                    row.map((val: number, j: number) =>
                        <Node
                            x={i}
                            y={j}
                            d={rectDiameter}
                            key={`${i}-${j}`}
                            state={val}
                            onMouseDown={handleMouseDown}
                            onMouseUp={handleMouseUp}
                            onHover={handleHover} />
                    )
                )
            }
        </div>
    );
}

export default Grid

在理解问题和建议解决方案方面的任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

状态更改是异步的(组件更改和挂接更改)。因此,在您致电paintNode时,您的状态尚未更新。我可以通过两种方法来解决它。一种方法是将呼叫paintNode放在nodeTypePointer的{​​{1}}中,如下所示:

useEffect

但是这意味着您需要将单击的 useEffect(() => { paintNode(x,y); }, [nodeTypePointer]) x存储在一些标准变量中,因为您将无法直接将它们传递给y。另一种方法(也许是一种更好的方法,因为我不是状态改变的忠实拥护者)在useEffect函数期间将x / y直接传递到paintNode中:

handleMouseDown

尽管看起来paintNode(x, y, grid[x][y]) 也需要x / y,所以也许最好保存原始点击状态,然后使用updateGridPosition单击该节点后,它将完成所有需要的操作。一切都取决于该功能的最终外观。

但是,是的,导致您的错误的原因是状态更改是异步的,因此在您致电useEffect时尚未更新