React钩子引入了useState来设置组件状态。但是我如何使用钩子来替换下面的回调代码:

setState(
  { name: "Michael" },
  () => console.log(this.state)
);

我想在状态更新后做一些事情。

我知道我可以使用useEffect来做额外的事情,但我必须检查之前的状态值,这需要位代码。我正在寻找一个简单的解决方案,可以使用useState挂钩。


当前回答

这个怎么样:

const [Name, setName] = useState("");
...
onClick={()=>{
setName("Michael")
setName(prevName=>{...}) //prevName is Michael?
}}

其他回答

你的问题很有道理。让我告诉您,useEffect在默认情况下运行一次,并在每次依赖数组更改之后运行一次。

请看下面的例子:

import React,{ useEffect, useState } from "react";

const App = () => {
  const [age, setAge] = useState(0);
  const [ageFlag, setAgeFlag] = useState(false);

  const updateAge = ()=>{
    setAgeFlag(false);
    setAge(age+1);
    setAgeFlag(true);
  };

  useEffect(() => {
    if(!ageFlag){
      console.log('effect called without change - by default');
    }
    else{
      console.log('effect called with change ');
    }
  }, [ageFlag,age]);

  return (
    <form>
      <h2>hooks demo effect.....</h2>
      {age}
      <button onClick={updateAge}>Text</button>
    </form>
  );
}

export default App;

如果你想要setState回调与钩子一起执行,那么使用标志变量并在useEffect中给出If ELSE OR If块,以便当条件满足时,只执行该代码块。无论如何,当依赖项数组改变时,效果会运行,但效果中的IF代码只会在特定的条件下执行。

UseEffect是主要的解决方案。但正如Darryl提到的,使用useEffect并将state作为第二个参数传入有一个缺陷,组件将在初始化过程中运行。如果你只想让回调函数使用更新后的状态值运行,你可以设置一个本地常量,并在setState和回调中使用它。

const [counter, setCounter] = useState(0);

const doSomething = () => {
  const updatedNumber = 123;
  setCounter(updatedNumber);

  // now you can "do something" with updatedNumber and don't have to worry about the async nature of setState!
  console.log(updatedNumber);
}

我认为,使用useEffect不是一种直观的方式。

我为此创建了一个包装器。在这个自定义钩子中,你可以将你的回调传递给setState参数,而不是useState参数。

我刚刚创建了Typescript版本。所以如果你需要在Javascript中使用它,只需从代码中删除一些类型符号。

使用

const [state, setState] = useStateCallback(1);
setState(2, (n) => {
  console.log(n) // 2
});

宣言

import { SetStateAction, useCallback, useEffect, useRef, useState } from 'react';

type Callback<T> = (value?: T) => void;
type DispatchWithCallback<T> = (value: T, callback?: Callback<T>) => void;

function useStateCallback<T>(initialState: T | (() => T)): [T, DispatchWithCallback<SetStateAction<T>>] {
  const [state, _setState] = useState(initialState);

  const callbackRef = useRef<Callback<T>>();
  const isFirstCallbackCall = useRef<boolean>(true);

  const setState = useCallback((setStateAction: SetStateAction<T>, callback?: Callback<T>): void => {
    callbackRef.current = callback;
    _setState(setStateAction);
  }, []);

  useEffect(() => {
    if (isFirstCallbackCall.current) {
      isFirstCallbackCall.current = false;
      return;
    }
    callbackRef.current?.(state);
  }, [state]);

  return [state, setState];
}

export default useStateCallback;

缺点

如果传递的箭头函数引用了一个变量外部函数,那么它将捕获当前值,而不是状态更新后的值。 在上面的使用示例中,console.log(state)将打印1而不是2。

我认为你需要的是useState和useCallback:

useState react doc; useCallback react doc;

示例代码

import React, { useCallback, useState } from 'react';

const Test = () => {
  const [name, setName] = useState("");
  const testCallback = useCallback(() => console.log(name), [name]);

  return (
    <button onClick={() => {
      setName("Michael")
      testCallback();
    }}>Name</button>
  )
};

export default Test;

简单的解决方案,只需安装

我使用-state-with-callback

import React from 'react';
import { useStateWithCallbackLazy } from "use-state-with-callback";

const initialFilters = {
  smart_filter: "",
};

const MyCallBackComp = () => {
  const [filters, setFilters] = useStateWithCallbackLazy(initialFilters);

  const filterSearchHandle = (e) => {
    setFilters(
      {
        ...filters,
        smart_filter: e,
      },
      (value) => console.log("smartFilters:>", value)
    );
  };

  return (
    <Input
      type="text"
      onChange={(e) => filterSearchHandle(e.target.value)}
      name="filter"
      placeholder="Search any thing..."
    />
  );
};

认为: 反应usestate回调