开发者问题收集

React hooks 状态未使用最新

2019-03-18
7058

我有以下代码,我想在其中创建一个标签列表。在此示例中,我在 setAllTags() 中获取标签列表,然后在 setAvailableTags() 中获取多个可用标签。

然后我遇到的问题是,当运行 setAvailableTags() 时,它将删除在 setAllTags() 中获取的标签。似乎当 setAvailableTags() 设置其状态时,我在 setAllTags() 中设置的状态未被使用。

知道我可以做些什么来解决这个问题吗?

https://codesandbox.io/s/rj40lz6554

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const Search = () => {
  const [tags, setTags] = useState({
    all: [],
    available: []
  });

  const setAllTags = () => {
    const all = ["tag 1", "tag 2", "tag 3"];
    const newValue = {
      ...tags,
      all
    };
    console.log("setting all tags to ", newValue);
    setTags(newValue);
  };

  const setAvailableTags = () => {
    const available = ["tag 1", "tag 2"];
    const newValue = {
      ...tags,
      available
    };
    console.log("setting available tags to", newValue);
    setTags(newValue);
  };

  useEffect(setAllTags, []);
  useEffect(setAvailableTags, []);

  return (
    <div>
      <div>
        <select placeholder="Tags">
          {tags.all.map((tag, i) => (
            <option key={tag + i} value={tag}>
              {tag}
            </option>
          ))}
        </select>
      </div>
    </div>
  );
};

const App = () => {
  return (
    <div>
      <h1>Hello React!</h1>
      <Search />
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("app"));

控制台记录了

setting all tags to Object {all: Array[3], available: Array[0]}
setting available tags to Object {all: Array[0], available: Array[2]}
3个回答

setTags 会更改内部反应状态,而不会直接更改 tags 的值。因此,直到下一次渲染时才会更新。

请改用此调用:

setTags(currentTags => ({...currentTags, all}));

并对 available 执行相同操作。

UjinT34
2019-03-18

在 React 文档中,你可以看到 useEffect 的工作原理( https://reactjs.org/docs/hooks-effect.html

Experienced JavaScript developers might notice that the function passed to useEffect is going to be different on every render. This is intentional. In fact, this is what lets us read the count value from inside the effect without worrying about it getting stale. Every time we re-render, we schedule a different effect, replacing the previous one. In a way, this makes the effects behave more like a part of the render result — each effect “belongs” to a particular render. We will see more clearly why this is useful later on this page.

这意味着,渲染函数中的每个副作用都只能访问 useState 的初始结果。请记住,React 状态是不可变的,因此更新状态,然后尝试在同一个渲染周期内使用更新后的版本是没有意义的。

您只需调用以下命令即可看到这一点:

setTags({ all: ['test'] })
console.log(tags)

您应该注意到 tags 根本没有改变。

我个人会在这里使用钩子约定,并将您的 useState 分成两个独立的变量:

const [allTags, setAllTags] = useState([])
const [availableTags, setAvailableTags] = useState([])

这使您的效果更加明确(因为它们只需要更新一个变量),并提高了可读性。

Matt Way
2019-03-18

来自: https://reactjs.org/docs/hooks-reference.html#usestate

Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:

setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});

Another option is useReducer, which is more suited for managing state objects that contain multiple sub-values.

如果您希望 setAvailableTags() 基于 setAllTags() ,您应该执行 useEffect(setAvailableTags, [tags.all])

工作示例: https://codesandbox.io/s/5vzr20x99p?from-embed

r g
2019-03-18