立即更新useState

问题描述 投票:0回答:6

useState
不会立即更新状态。

我正在使用react-select,我需要使用根据请求结果选择的(

multi
)选项来加载组件。

因此,我创建了状态

defaultOptions
,来存储
queues
常量的值。

事实证明,加载组件时,只有第二次才显示值。

我在

console.log
中做了一个
queues
,返回和空不同

我对

defaultOptions
状态做了同样的事情,返回为空。

我创建了一个codesandbox以便更好地查看。

const options = [
  {
    label: "Queue 1",
    value: 1
  },
  {
    label: "Queue 2",
    value: 2
  },
  {
    label: "Queue 3",
    value: 3
  },
  {
    label: "Queue 4",
    value: 4
  },
  {
    label: "Queue 5",
    value: 5
  }
];

const CustomSelect = (props) => <Select className="custom-select" {...props} />;

const baseUrl =
  "https://my-json-server.typicode.com/wagnerfillio/api-json/posts";

const App = () => {
  const userId = 1;
  const initialValues = {
    name: ""
  };
  const [user, setUser] = useState(initialValues);
  const [defaultOptions, setDefaultOptions] = useState([]);
  const [selectedQueue, setSelectedQueue] = useState([]);

  useEffect(() => {
    (async () => {
      if (!userId) return;
      try {
        const { data } = await axios.get(`${baseUrl}/${userId}`);
        setUser((prevState) => {
          return { ...prevState, ...data };
        });

        const queues = data.queues.map((q) => ({
          value: q.id,
          label: q.name
        }));

        // Here there is a different result than emptiness
        console.log(queues);
        setDefaultOptions(queues);
      } catch (err) {
        console.log(err);
      }
    })();

    return () => {
      setUser(initialValues);
    };
  }, []);

  // Here is an empty result
  console.log(defaultOptions);

  const handleChange = async (e) => {
    const value = e.map((x) => x.value);
    console.log(value);
    setSelectedQueue(value);
  };

  return (
    <div className="App">
      Multiselect:
      <CustomSelect
        options={options}
        defaultValue={defaultOptions}
        onChange={handleChange}
        isMulti
      />
    </div>
  );
};
export default App;
reactjs react-hooks react-select
6个回答
7
投票

React不会在调用setState时立即更新状态,有时可能需要一段时间。如果您想在设置新状态后执行某些操作,可以使用 useEffect 来确定状态是否更改,如下所示:

    const [ queues, setQueues ] = useState([])

    useEffect(()=>{
        /* it will be called when queues did update */
    },[queues] )

    const someHandler = ( newValue ) => setState(newValue)

3
投票

setState 的闭包和异步性质

您正在经历的是 closures(渲染期间如何在函数内捕获值)和 setState 的异步性质的组合。

请参阅此Codesandbox以获取工作示例

考虑这个测试组件

const TestComponent = (props) => {
  const [count, setCount] = useState(0);

  const countUp = () => {
    console.log(`count before: ${count}`);
    setCount((prevState) => prevState + 1);
    console.log(`count after: ${count}`);
  };

  return (
    <>
      <button onClick={countUp}>Click Me</button>
      <div>{count}</div>
    </>
  );
};

测试组件是您用来说明闭包和 setState 异步性质的简化版本,但这些想法可以推断到您的用例。

渲染组件时,每个函数都会创建为闭包。考虑第一次渲染时的函数

countUp
。由于 count 在
useState(0)
中初始化为 0,因此将所有
count
实例替换为 0,看看它在初始渲染的闭包中是什么样子。

 const countUp = () => {
    console.log(`count before: ${0}`);
    setCount((0) => 0 + 1);
    console.log(`count after: ${0}`);
  };

记录设置计数之前和之后的计数,可以看到设置计数之前和“设置”计数之后两个日志都会显示0。

setCount 是异步的,这基本上意味着:调用 setCount 会让 React 知道它需要安排渲染,然后它将修改 count 的状态并使用下一个渲染上的 count 值更新闭包。

因此,初始渲染将如下所示

 const countUp = () => {
    console.log(`count before: 0`);
    setCount((0) => 0 + 1);
    console.log(`count after: 0`);
  };

当调用 countUp 时,该函数将记录创建该函数闭包时

count
的值,并让 React 知道它需要重新渲染,因此控制台将如下所示

count before: 0 
count after: 0 

React 将重新渲染并因此更新

count
的值,并重新创建 countUp 的闭包,如下所示(替换 count 的值)。然后,这将使用最新的 count 值更新任何可视组件,以显示为
 1

 const countUp = () => {
    console.log(`count before: 1`);
    setCount((1) => 1 + 1);
    console.log(`count after: 1`);
  };

并且每次单击计数按钮时都会继续这样做。

这是来自 codeSandbox 的片段。请注意控制台如何从初始渲染关闭控制台日志中记录

0
,但由于 UI 的异步渲染,单击一次后
count
的显示值显示为
1

如果您希望查看该值的最新渲染版本,最好使用 useEffect 来记录该值,一旦调用 setState,这将在 React 的渲染阶段发生

useEffect(() => {
  console.log(count); //this will always show the latest state in the console, since it reacts to a change in count after the asynchronous call of setState.
},[count])

2
投票

添加其他答案:

在类组件中,您可以在添加新状态后添加回调,例如:

  this.setState(newStateObject, yourcallback)

但在函数组件中,您可以在某些值更改后调用“回调”(不是真正的回调,而是某种形式),例如

// it means this callback will be called when there is change on queue.
React.useEffect(yourCallback,[queue])
.
.
.

// you set it somewhere
 setUserQueues(newQueues);

你就可以开始了。

别无选择(除非你想

Promise
)但是
React.useEffect


0
投票

您需要在

useEffect
挂钩内使用参数,并仅在进行某些更改时重新渲染。下面是一个带有
count
变量的示例,并且仅当计数值发生变化时钩子才会重新渲染。

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

0
投票

问题是,await api.get() 将返回一个承诺,因此当 setUserQueues(queues); 行时,常量数据不会拥有其数据集。正在运行。

你应该这样做:

 api.get(`/users/${userId}`).then(data=>{

  setUser((prevState) => {
    return { ...prevState, ...data };
  });

  const queues = data.queues.map((q) => ({
    value: q.id,
    label: q.name,
  }));
  setUserQueues(queues);

  console.log(queues);
  console.log(userQueues);});

0
投票

如果您想更新状态并在同一函数中使用更新后的值,那么您可以在设置状态时将值存储在变量中。

const [count, setCount] = useState(0)

const incrementCount = () => {
 let updatedCount;
 setCount((prevCount) => {
   const newCount = prevCount + 1;
   updatedCount = newCount;
   return newCount; // Return the new value
 })
 console.log(updatedCount)
};
© www.soinside.com 2019 - 2024. All rights reserved.