我了解useState是异步的。请在回答之前阅读完整的问题。

我正在尝试使用useState修改数组中的元素,但是它没有按预期工作。

数组及其修改函数:

const [table, setTable] = useState(['blue', 'blue', 'blue', 'blue', 'blue']);

let currentShapePosition = 2;

function printTable() {
  let newTable = [...table];
  // let newTable = table;
  newTable[currentShapePosition] = 'red';
  setTable(newTable);
  console.log('printTable newTable', newTable); // <-- the result is as expected
  // log => printTable newTable ["blue", "blue", "red", "blue", "blue"]
  console.log('printTable table', table); // <--- The problem is here. I don't get why the array never update
  // log => printTable newTable ["blue", "blue", "blue", "blue", "blue"]
}

因为useState是异步的,所以我知道数组可能不会立即更改,但是在printTable函数内部,即使多次重新提交,console.log 的结果也是相同的。

什么时候代替:let newTable = [...table]
我这样做:let newTable = table
然后,在函数中生成的console.log中更新状态,但是没有重新渲染/组件更新。

我想理解为什么在多次重新渲染之后,console.log函数内部的newTable = [...table]在第一种情况下是
以及为什么在第二种情况下 newTable = table中,尽管使用setTable(newTable)却没有重新呈现组件。
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./style.css";

const App = () => {
  let currentShapePosition = 2;

  const [table, setTable] = useState(["blue", "blue", "blue", "blue", "blue"]);

  useEffect(() => {
    printTable();
    window.addEventListener("keydown", handleKeyPress);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    console.log("render", table);
  });

  function printTable() {
    let newTable = [...table];
    // let newTable = table;
    newTable[currentShapePosition] = "red";
    setTable(newTable);
    console.log("printTable newTable", newTable); // <-- the result is as expected
    console.log("printTable table", table); // <--- The problem is here. I don't get why the initial value never change
  }

  function handleKeyPress(event) {
    switch (event.key) {
      case "Left": // IE/Edge specific value
      case "ArrowLeft":
        moveShape(-1);
        break;
      case "Right": // IE/Edge specific value
      case "ArrowRight":
        moveShape(1);
        break;
      default:
        return;
    }
  }

  function moveShape(direction) {
    currentShapePosition += direction;
    printTable();
  }

  return (
    <table className="tetris-table">
      <tbody>
        <tr>
          <td className={table[0]} />
          <td className={table[1]} />
          <td className={table[2]} />
          <td className={table[3]} />
          <td className={table[4]} />
        </tr>
      </tbody>
    </table>
  );
};

const root = document.getElementById("root");

ReactDOM.render(<App />, root);

javascript - 渲染后记录状态的结果相同-LMLPHP

最佳答案

问题是您的第一个useEffect,因为您删除了eslint警告,它隐藏了潜在的错误。

useEffect(() => {
  printTable();
  window.addEventListener('keydown', handleKeyPress);
  //   v hidden bug, you should consider the warnings
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

此处发生的是,在组件上安装了,这是分配给handleKeyPressaddEventListenerclosures的第一个实例(请参阅 currentPosition ),其中所有数组的值均为“blue”,并且它将一直保持这种状态直到卸载。

您应该知道,在执行组件主体的每个渲染
,因此,在您的情况下,每个函数都有一个新实例。

应该作为引用的eslint也是如此。

要解决此问题,请删除ojit_code注释并附带警告:
useEffect(() => {
  const printTable = () => {
    let newTable = [...table];
    newTable[currentPosition.current] = 'red';
    setTable(newTable);
    console.log('closure', table);
  };

  function handleKeyPress(event) {
    switch (event.key) {
      case 'Left': // IE/Edge specific value
      case 'ArrowLeft':
        moveShape(-1);
        break;
      case 'Right': // IE/Edge specific value
      case 'ArrowRight':
        moveShape(1);
        break;
      default:
        return;
    }
  }

  function moveShape(direction) {
    currentPosition.current += direction;
    printTable();
  }

  window.addEventListener('keydown', handleKeyPress);

  return () => window.removeEventListener('keydown', handleKeyPress);
}, [table]);

javascript - 渲染后记录状态的结果相同-LMLPHP

关于javascript - 渲染后记录状态的结果相同,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58329134/

10-09 15:14