【问题标题】:React DOM not updating on state changeReact DOM 不更新状态变化
【发布时间】:2020-05-15 18:25:29
【问题描述】:

我有一个反应组件,它可以接受任意数量的文件,每个文件都有自己的语言。我希望能够有一个按钮,让用户可以根据需要添加任意数量的文件,每个文件都有自己的语言下拉菜单。由于我隔离了我遇到的问题,我现在已经消除了文件上传。

我已经创建了很多类似的组件,没有任何问题,但由于某种原因,我在这个组件上遇到了很大的麻烦。当我在下拉菜单中更改语言时,我控制台日志状态并看到语言已更新状态,但它没有在我的 map 方法中的 DOM 上更新。

这是我的代码

function App() {
  const [files, setFiles] = useState([]);

  const languages = ["English", "Spanish", "Arabic", "French"];

  const handleAddFile = e => {
    let newFiles = files;
    newFiles.push({
      content: null,
      preview: null,
      language: ""
    });
    setFiles(newFiles);
  };

  const changeLanguage = (event, index) => {
    let newFiles = files;
    newFiles[index].language = event.target.value;
    setFiles(newFiles);
  };

  console.log(files);

  return (
    <div>
      <p>Files</p>
      <p>Upload a file for as many languages as you have</p>
      {files.map((file, index) => (
        <div key={index}>
          <p>current language: {file.language}</p>
          <select
            value={file.language}
            onChange={event => changeLanguage(event, index)}
          >
            <option value="" disabled>
              Select a language
            </option>
            {languages.map((lang, i) => (
              <option value={lang} key={i}>
                {lang}
              </option>
            ))}
          </select>
        </div>
      ))}
      <button onClick={e => handleAddFile(e)}>+ Add File</button>
    </div>
  );
}

我还制作了一个代码框来重现该问题,但它实际上使事情变得更加复杂。当我在本地运行代码时,我可以很好地添加文件实例,但是 select 元素上的语言不会在任何实例的 DOM 上更新。在代码框中,除非我在编辑器中进行编辑,否则 DOM 上没有任何更新,然后一切都会按照我的预期进行更新,包括语言。在所有情况下,状态都完全按照我的预期进行控制台记录。沙盒中的组件与我在本地的组件相同,但在本地它嵌入在另一个组件中。

https://codesandbox.io/s/ed4fx?file=/src/App.js:0-1248

【问题讨论】:

  • 我已经分叉了你的密码箱codesandbox.io/s/hungry-gagarin-il35g?file=/src/App.js,如果它对你有用,请告诉我。
  • @7evam handleAddFile 和 changeLanguage 都改变状态。这将防止重新渲染,因为 react 不会看到状态已更改。
  • 我已经在答案中添加了代码更新部分和解释

标签: javascript reactjs react-hooks codesandbox


【解决方案1】:

问题出在handleAddFile 和changeLanguage 两种方法中,您直接访问状态值文件,因此要更新,您需要获取副本并进行更改并设置新值。

您需要在这两种方法中进行的更新

let newFiles = files;

到

let newFiles = [...files];

正如@Brian 建议的那样,当您在changeLanguage 方法中更新深层嵌套值(如语言)时。您可以执行如下所示的操作,这样它就不会改变状态。

const changeLanguage = (event, index) => {
  let newFiles = [...files];
  newFiles[index] = { ...newFiles[index], language: event.target.value };
  setFiles(newFiles);
};

检查这个工作codesandbox

更好的解释here

【讨论】:

  • changeLanguage 还应该考虑深度嵌套的状态值。即使在正确解构files 之后,执行newFiles[index].language = event.target.value; 也会改变嵌套对象(即使它可能不会阻止重新渲染)
  • 是的,行得通!我知道我不能直接改变状态,但现在我知道我在不知不觉中是。感谢您的帮助!
  • In this answer, under the Explanation heading 非常详细地解释了如何在 JS 中制作对象(和数组)的真实副本
  • @BrianThompson ,我希望这是你所期望的,纠正我这将添加答案 let newFiles = [...files];让 currentFile = newFiles[index]; const { value } = event.target currentFile = {...currentFile, language: value}; newFiles[index] = currentFile setFiles(newFiles);
  • 它看起来不必要地复杂,但我认为它有效
猜你喜欢
  • 2020-12-10
  • 2016-10-02
  • 2018-04-08
  • 1970-01-01
  • 1970-01-01
  • 2020-11-23
  • 1970-01-01
  • 1970-01-01
  • 2016-11-10
相关资源
最近更新 更多