React + useState() + previousState

7

我对React比较陌生,不太理解useState Hook,尤其是previousState方面的内容。

一个普通的useState Hook,也许是最常见的例子,看起来像这样:

import React, { useState} from 'react';

export default function CounterHooks({ initialCount }){
  const [count, setCount] = useState(initialCount);

  return (
    <div>
      <button onClick={() => setCount(count -1)}>-</button>
      <span>{count}</span>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  )
}

到目前为止,我了解到的是:
  • 我调用useState()函数/钩子(hook)并传入初始状态的参数(initialCount)
  • 返回一个数组,我立即将其解构为变量count和setCount(这是一个函数)
  • 我可以使用setCount()更新状态,从而更新count变量
目前为止还算顺利,我想... ;)
有时我会看到相同的计数器示例与prevState(prevCount),但我不理解:
<button onClick={() => setCount(prevCount => prevCount -1)}>-</button>

这里发生了什么?我不理解这部分。

  • 在这种情况下,我以某种方式访问了先前的计数值。
  • setCount 现在需要一个函数。
  • 现在异步运行 setCount。
  • 这个函数是从哪里来的?
  • prevCount 是从哪里来的?
  • 当我运行这个时,什么被放入 prevCount 中?

你能理解我的困惑吗?我不确定我应该如何以不同的方式表达它...

非常感谢你的帮助。


2
你说的一切都是正确的。状态设置函数可以接收一个函数而不是一个值。如果setter接收到一个函数,它将调用该函数以使用函数的返回值设置状态。此外,它将把“previousState”作为参数传递给该函数。这就是你可以根据先前的状态更改状态的方法。请在此处查找更详细的答案:https://dev59.com/M1MI5IYBdhLWcg3wXaTZ - Andre
嗨,安德烈,感谢你的帮助。我在文档中读到了这个内容,但是我不理解它:https://reactjs.org/docs/hooks-reference.html#functional-updates。我不明白的是:我需要传递什么函数给setState()/setCount()。 - Fabian
1
我不明白的是:我要传入什么样的函数到setState()/setCount()中。需要传入一个接受单一参数(即先前的状态值)并返回新值的函数(可能基于先前的值,也可能不基于)。也许关于setState的文档可以更清楚地解释(在末尾处):https://reactjs.org/docs/react-component.html#setstate。如果新状态不依赖于先前状态,则没有必要传递函数。 - Felix Kling
2个回答

11

首先,您可以在这里看到官方说明。

在这种情况下,我不知道如何访问以前的计数值。

您并不是“不知道如何”访问以前的计数值。如果您使用函数更新并为setter提供回调函数,则会将上一个状态返回给您,并在回调函数中使用此值。

现在setCount需要一个函数

由于您提供了一个函数,因此它可以使用它。

setCount现在是异步运行的

实际上不是。这不是异步函数。它只提供了先前的状态,您可以使用它。状态设置器不是异步的,状态更新是。

这个函数从哪里来的?

prevCount来自哪里?

已经回答了。

当我运行这个时,prevCount中放了什么?

您可以在那里提供要设置新状态的内容。在您的示例中,您希望将count增加1,因此您正在提供+ 1

这里是这种逻辑的一个天真的解释。只是一个天真的解释。 我添加这个作为回调逻辑示例,与React的setState无关。

let state = 5;

function setState(val) {
  if (typeof val === "function") {
    return val(state);
  }

  return val;
}

const stateWithFunctionalUpdate = setState(prev => prev + 1);
const stateWithNormalUpdate = setState(9);
console.log(stateWithFunctionalUpdate);
console.log(stateWithNormalUpdate);

也许这个例子适合模仿React的状态设置逻辑。再次强调,这只是一个天真的方法。

let state = 5;

function setState(val) {
  if (typeof val === "function") {
    state = val(state);
  } else {
    state = val;
  }

}

setState(9);
setState(prev => prev + 1);

console.log("state", state);

让我们来看一下useState真实实现(不带类型):

export function useState(initialState) {
  return useReducer(
    basicStateReducer,
    // useReducer has a special case to support lazy useState initializers
    initialState
  );
}

嗯,它只是在幕后使用useReducer。 它设置初始状态并使用basicStateReducer,它是:

function basicStateReducer(state, action) {
  return typeof action === "function" ? action(state) : action;
}

就像我们的朴素方法一样,它寻找action的类型并根据其行为进行处理。最终,这就是useReducer 返回的内容

return [workInProgressHook.memoizedState, dispatch];

因此,归根结底,如果我们向useState提供一个函数,它会在我们的状态上运行该函数,并将值返回。


1
嗨,devserkan,非常感谢您详细的回复。这非常有帮助。我现在明白了。我将一个回调函数传递给useState。useState调用该函数。回调函数期望一个参数,setCount使用变量count的值提供该参数。 - Fabian
1
不客气。嗯,你几乎是对的。你没有将回调函数传递给useState,而是传递给setStatesetCounter或者其他的setter :) - devserkan

2
这是我的理解和意见:

在这种情况下,我以某种方式访问了先前的计数值。

-> setCount将检查参数,如果参数是回调函数,则将调用该回调函数并传递先前状态作为参数。回调的结果将成为下一个状态。

setCount现在需要一个函数 -> 是的,函数处理您的逻辑并返回下一个状态。

setCount现在是异步运行的 -> 这个函数是同步的,但在返回结果后。 setCount的钩子将分派事件以更新下一个状态。

这个函数从哪里来? -> 您可以在函数中执行任何操作,只要参数是当前状态并返回下一个状态。

prevCount来自哪里? -> 当调用setCount时将传递(它从当前状态获取)。

当我运行它时,prevCount中放置了什么? -> setCount的当前状态。

在这里传递值或回调函数以设置计数的示例:useState钩子,setState函数。访问先前的状态值

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接