我如何同步钩子

时间:2021-04-11 07:14:03

标签: reactjs react-hooks

过去,我写的 reactjs 代码是这样的:

  class App extends Component {
    state = { var1:null, var2:null, var3:null };
    
    myfunction1 = async () => {
       ...
       this.setState({ var1:10, var2:20, var3:30 }, this.myfunction2); 
    }

    myfunction2 = async () => {
       ...
    };

    render() {
       console.log("render");
       return (
          <div className="App">
            { this.state.var1 }
            { this.state.var2 }
            { this.state.var3 }
          </div>
       );
    }

现在,我是这样工作的:

const App = () => {
  const [var1, setVar1] = useState(null);
  const [var2, setVar2] = useState(null);
  const [var3, setVar3] = useState(null);

  async function init() {
     setVar1(10);
     setVar2(20);
     setVar3(30);
     function2();
  }

  useEffect(() => { init(); }, []);

  async function function2() {

  }

  console.log("render");
  return (
          <div className="App">
            { var1 }
            { var2 }
            { var3 }
          </div>
       );

这是我在第二种方式中的问题:

  • 有没有办法在同一个调用中设置 setVar1、setVar2 和 setVar3?问题是页面将刷新 3 次。在第一次刷新时,var1 将等于 10,但 var2 和 var3 将为空。它可能会导致一些问题...

  • 我如何确定在设置 var1、var2 和 var3 之后会调用 function2?在第一种方法中有一个回调函数,只有在设置状态时才会调用它。我如何在第二种方法中做同样的事情?

谢谢

3 个答案:

答案 0 :(得分:2)

请注意,您的两个示例不等价,您需要有一个状态对象,这样它们才会是。

<块引用>

有没有办法在同一个调用中设置 setVar1、setVar2 和 setVar3?

您实际上是在问 how to batch state changes,React 不会像您 (async) 那样批量承诺调用

要么将 promise 调用更改为普通调用,要么尝试使用单个状态对象等通用解决方案。

useState({ var1: null, var2: null, var3: null })
<块引用>

我如何确定 function2 会在 init 之后被调用

您正在使用异步调用,因此只需在第一个调用解决后调用 function2。或者,您可以使用布尔值引用来指示您在 init 调用,similar logic on having useEffect stop running after mount 之后。

完整示例:

const App = () => {
  const [state, setState] = useState({ var1: null, var2: null, var3: null });

  // or
  const isInitCalled = useRef(false);

  function function2() {
    console.log("after");
  }

  useEffect(async () => {
    async function init() {
      setVar1({ var1: 10, var2: 20, var3: 30 });
      function2();
    }

    await init();
    function2();
  }, []);

  // Or
  useEffect(() => {
    init();
    isInitCalled.current = true;
  }, []);

  useEffect(() => {
    if (isInitCalled.current) {
      func2();
    }
  }, [state]);

  return (
    <div className="App">
      {var1}
      {var2}
      {var3}
    </div>
  );
};

答案 1 :(得分:0)

您可以使用数组或对象代替原始值:

const App = () => {
  const [vars, setVars] = useState([null, null, null]);

  async function init() {
     setVars([10, 20, 30]);
     function2();
  }

  useEffect(() => { init(); }, []);

  async function function2() {

  }

  console.log("render");
  return (
          <div className="App">
            { vars[0] }
            { vars[1] }
            { vars[2] }
          </div>
       );

当然,如果您的实际“变量”没有编号,您可能更喜欢带有属性的对象而不是数组。

答案 2 :(得分:0)

<块引用>

有没有办法在同一个调用中设置 setVar1、setVar2 和 setVar3?问题是页面将刷新 3 次。在第一次刷新时,var1 将等于 10,但 var2 和 var3 将为空。它可能会导致一些问题...

首先,我只想指出它不一定要渲染多次。如果代码在 react 中开始执行(例如,useEffect 或合成事件),那么 react 将批量处理多个状态更改并且只执行一次重新渲染。但是确实在某些情况下,react 并没有开始执行,所以执行不会返回到react,所以react 只能立即重新渲染。例如,如果您在 setTimeout 回调中或在 await 承诺之后设置状态,就会发生这种情况。

如果只需要一次重新渲染就需要多次设置状态,可以使用unstable_batchedUpdates

import { unstable_batchedUpdates } from 'react';

// ...
async function init() {
   unstable_batchedUpdates(() => {
     setVar1(10);
     setVar2(20);
     setVar3(30);    
   })

   function2();
}

术语“不稳定”是 React 团队的说法,这个 api 可能会在 React 的下一个主要版本中发生变化。但是使用起来是完全安全的,只要你在升级 React 版本时注意这一点。

<块引用>

我如何确定在设置 var1、var2 和 var3 之后会调用 function2?在第一种方法中有一个回调函数,只有在设置状态时才会调用它。我如何在第二种方法中做同样的事情?

在功能组件中设置状态并不能知道组件何时重新渲染。这可能是因为它不会有任何好处。没有发生变异的 this,因此无论经过多少时间,function2 都不会看到新值。它的闭包中包含来自当前渲染的值,并且无法访问下一个渲染。

相反,修复最有可能将新值传递给 function2。例如,如果它需要知道 var1 变为 10,则需要将其传递给 10。

async function init() {
   const newVar1 = 10;
   unstable_batchedUpdates(() => {
     setVar1(newVar1);
     setVar2(20);
     setVar3(30);    
   })

   function2(newVar1);
}

async function function2(newVar1) {

}