1

我正在尝试通过构建 Web 应用程序来学习 React。由于我想一步一步地学习,所以目前我不使用 Redux,我只使用 React 状态,我有一个问题。

这是我的组件架构:

           App.js
             |
    _________|_________
   |                   |
Main.js              Side.js
   |                   |
Game.js              Moves.js

如您所见,我有一个名为 的主文件App.js,在左侧我们有Main.js一个应用程序的中心部分,其中包含Game.js我的游戏实际发生的位置。在右侧,我们有Side.js一个侧边栏,我想在其中显示每个玩家在游戏中所做的动作。它们将显示在Moves.js.

为了更清楚地思考国际象棋游戏。在左边你实际玩游戏,在右边你的动作将被列出。

现在我将向您展示我的代码并解释问题所在。

// App.js

const App = React.memo(props => {     
    let [moveList, setMovesList] = useState([]);

    return (
        <React.Fragment>  
            <div className="col-8">                                            
                <Main setMovesList={setMovesList} />                        
            </div>
            <div className="col-4">  
                <Side moveList={moveList} />
            </div>    
        </React.Fragment>
    );
});

// Main.js

const Main = React.memo(props => { 
    return (
        <React.Fragment>                
            <Game setMovesList={props.setMovesList} />
        </React.Fragment>
    );
});

// Game.js

const Game= React.memo(props => { 
    useEffect(() => {
        function executeMove(e) {
            props.setMovesList(e.target);
        }

        document.getElementById('board').addEventListener('click', executeMove, false);    

        return () => {            
            document.getElementById('board').removeEventListener('click', executeMove, false);              
        };
    }) 
    
    return (
        // render the game board
    );
});

// Side.js

const Side= React.memo(props => { 
    return (
        <React.Fragment>                
            <Moves moveList={props.moveList} />
        </React.Fragment>
    );
});

// Moves.js

const Moves= React.memo(props => { 
    let [listItems, setListItems] = useState([]);

    useEffect(() => {
        let items = [];                    

        for (let i = 0; i < props.moveList.length; i++) {
            items.push(<div key={i+1}><div>{i+1}</div><div>{props.moveList[i]}</div></div>)                                          
        }

        setListItems(items);

        return () => { 
            console.log('why this is being triggered on each move?') 
        }; 

    }, [props.moveList]);

    return (
        <React.Fragment>                
            {listItems}  
        </React.Fragment>
    );
});

正如您在我的代码中看到的那样,我已经在App.js. 在左侧,我传递了根据玩家的动作更新状态的函数。在右侧,我传递状态以更新视图。

Game.js我的问题是,组件内的每个单击事件都会Moves.js卸载并且console.log正在被触发,我没想到它会表现得像那样。我期待它只会在我将视图更改为另一个视图时卸载。

知道为什么会这样吗?如果我写的东西没有意义,请随时问我。

4

3 回答 3

2

感谢您很好地解释了您的问题-这真的很容易理解。

现在,问题是,您的组件实际上并没有卸载。您已将 props.movesList 作为 usEffect 的依赖项传递。现在第一次触发你的 useEffect 时,它会设置 return 语句。下次由于 props.movesList 的变化而触发 useEffect 时,将执行 return 语句。

如果您打算在卸载组件时执行某些操作 - 将其转移到另一个具有空依赖数组的 useEffect 。

于 2021-02-06T10:21:06.583 回答
1

回答你的问题

您的问题
“为什么每次移动
都会触发”的答案是:
“因为 useEffect 想要使用更改的状态更新组件”

但我倾向于说: “你不应该问这个问题,你不应该关心”

理解useEffect

您应该将其理解useEffect为确保状态是最新的东西,而不是一种生命周期钩子

想象一个时刻,一遍useEffect又一遍地被调用,只是为了确保一切都是最新的。这不是真的,但这种心智模型可能有助于理解。

您不关心是否以及何时useEffect被调用,您只关心状态是否正确。

从返回的函数useEffect应该清理它自己的东西(例如事件监听器),再次确保一切都是干净和最新的,但它不是 onUnmount处理程序。

理解 React 钩子

您应该习惯这样的想法,即每个功能组件和每个钩子都被一遍又一遍地调用。React 决定是否不需要。

如果你真的有性能问题,你可能会使用 eg React.memoand useCallback,但即便如此,也不要依赖于任何东西都不再被调用。如果 React 认为有必要,它可能会调用你的函数。React.memo仅用作一种提示,以对此处进行一些优化做出反应。

更多反应技巧

  • 工作状态
  • 显示状态

例如,不要<div>像您那样创建一个列表,而是创建一个例如对象的列表,并在视图中呈现该列表。你甚至可以创建一个自己的MovesView组件,只显示列表。在您的示例中,这可能有点过多的分离,但是您应该习惯这个想法,而且我认为您的真实组件最终会更大。

不要害怕将组件拆分成更小的组件。

于 2021-02-06T12:45:05.330 回答
0

看来问题是由游戏元素发生的。

它在每次渲染时触发 addEventListener。

为什么不使用onClick事件处理程序

/* remove this part
 useEffect(() => {
        function executeMove(e) {
            props.setMovesList(e.target);
        }

        document.getElementById('board').addEventListener('click', executeMove, false);    
    }) 

*/

const executeMove = (e) => {
     props.setMovesList(e.target);
}

return (
    <div id="board" onClick={executeMove}>
    ...
    </div>
)

如果要使用 addEventListener,则应在组件挂载时添加。useEffect将空数组([])作为第二个参数传递给。

 useEffect(() => {
        function executeMove(e) {
            props.setMovesList(e.target);
        }

        document.getElementById('board').addEventListener('click', executeMove, false);    
    }, []) 
于 2021-02-06T10:13:03.927 回答