反应初始化和与后端同步状态

Posted

技术标签:

【中文标题】反应初始化和与后端同步状态【英文标题】:React Initializing and synchronizing state with backend 【发布时间】:2020-06-04 18:55:35 【问题描述】:

我想知道初始化状态并使其与服务器保持同步的最佳方式/模式是什么。在过去的几天里,我已经阅读并尝试了很多,但没有找到任何可以解决我问题的东西。

这个例子很简单。有一个状态——为了简单起见,它在示例中是一个数字,尽管在现实生活中它是一个对象——我需要从服务器中检索它。检索后,我希望它与服务器同步。 getValueFromServer 是一个模拟,它在 setTimeout 中等待随机时间后返回一个随机值。

所以,为了初始化我的状态,我在一个空数组上使用了一个 useEffect 作为依赖项,为了保持同步,我使用了一个将状态作为依赖项的 useEffect。

问题是它试图保存一个未定义的值。日志如下。

1 -> 第一次初始化

2 -> API 调用保存在服务器值:未定义

3 -> API 调用保存在服务器值:6.026930847574949

我得到了什么:

1:按预期在安装时运行。

2:这是我没想到的。我猜它是因为“useState”而触发的。

3:一旦我们从服务器获得响应就运行。有点明显但很痛苦,因为我到底为什么要保存这个。

这里最好的方法是什么?在具有依赖关系的 useEffect 中使用“isInitialized”标志之类的东西感觉有点被黑而不专业。

下面的代码,你也可以在这里找到它:https://codesandbox.io/s/optimistic-rgb-uce9f

import React,  useState, useEffect  from "react";
import  getValueFromServer  from "./api";

export default function App() 
  const [value, setValue] = useState();

  useEffect(() => 
    async function initialize() 
      console.log("Initializing for first time");
      let serverValue = await getValueFromServer();
      setValue(serverValue);
    
    initialize();
  , []);

  useEffect(() => 
    console.log("API call to save in server value: ", value);
  , [value]);

  const handleClick = () => 
    setValue(value + 1);
  ;

  return (
    <div className="App">
      <h1>Value: value</h1>
      <button onClick=handleClick>Add 1 to value</button>
    </div>
  );

【问题讨论】:

【参考方案1】:

这里最好的方法是什么?使用类似的东西 具有依赖性的useEffect中的“isInitialized”标志感觉有点 被黑而不专业。

您可以在useState 中使用标志或使用默认值初始化对象

const  Fragment, useState, useEffect  = React;

const getValueFromServer = () => new Promise((resolve, reject) => setTimeout(() => resolve(Math.random())), 1000)

const App = () => 
  const [value, setValue] = useState(null);
  const [isLoading, setLoading] = useState(true);

  useEffect(() => 
    let isUnmounted = false;
  
    getValueFromServer().then(serverValue => 
      console.log("Initializing for first time");
      if(isUnmounted) 
        return;
      
      setValue(serverValue);
      setLoading(false);
    )
    
    return () => 
      isUnmounted = true;
    
  , []);

  useEffect(() => 
    if(!value) 
      return () => 
    
  
    console.log("API call to save in server value: ", value);
    setTimeout(() => setLoading(false), 50);
  , [value]);

  const handleClick = () => 
    setLoading(true);
    setValue(value + 1);
  ;

  return <div className="App">
    isLoading ? <Fragment>
      <span>Loading...</span>
    </Fragment> : <Fragment>
      <h1>Value: value</h1>
      <button onClick=handleClick>Add 1 to value</button>
    </Fragment>
  </div>


ReactDOM.render(
    <App />,
    document.getElementById('root')
  );
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.10.1/polyfill.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>

【讨论】:

嗨!非常感谢。很简洁的回答。我看到使用标志是不可避免的。我将不得不使用另一个标志来避免调用服务器来存储我第一次检索到的值。谢谢,很有帮助!【参考方案2】:

您的应用程序在忙时会做其他事情await在您的第一个 useEffect 钩子函数中发生一些事情。在这种情况下,“其他东西”是“执行你的第二个 useEffect 钩子函数”。 serverValue,因此value,此时未定义,因此您的console.log 打印undefined。一旦您的 await 承诺解析为一个值并且 setValue 被调用,您的第二个 useEffect 钩子函数的依赖项会发生变化,导致您的函数第二次运行(并打印出您期望的值)。

如果您必须有两个 useEffect 挂钩,那么当 value 未定义时,只需退出第二个:

  useEffect(() => 
    if (value === undefined) 
      return;
    

    console.log('value is:', value);
  , [ value ]);

【讨论】:

我会留下这个作为评论,或者投票结束这个问题作为一个骗子,但我有点惊讶地找不到一个现有的回答 SO 问题谈论多个 useEffect 钩子。也许其他人会有更好的运气? 您好,感谢您的评论。我知道它在等待时正在做事。我真正感兴趣的是你的最后一行,似乎价值检查或标记是不可避免的。谢谢!

以上是关于反应初始化和与后端同步状态的主要内容,如果未能解决你的问题,请参考以下文章

Beta实际开发与初始计划的比较

在PC中运行的IDE与后端UNIX机器中的代码之间的代码同步

部署时socket.io服务器是不是需要与后端分开?

如何从 props 初始化反应功能组件状态

反应中没有构造函数的初始化状态

为啥不能根据道具反应设置初始状态