从 useState 中的异步函数获取值

Posted

技术标签:

【中文标题】从 useState 中的异步函数获取值【英文标题】:get value from async function in useState 【发布时间】:2021-11-24 10:04:29 【问题描述】:

我正在使用async storage 来检索我的数据,但如何将它与 useState() 一起使用?

例子:

async function getdata()
let d= await AsyncStorage.getItem('Name');
return d;


export default function App() 
 const [name, setName] = useState(() => getdata());
 return (<View>...</View>)

但这不起作用,因为 getdata() 是异步的,那么我该如何解决这个问题?

编辑:

我忘了说我试过 useEffect() Hook 是这样的:

async function getdata()
let d= await AsyncStorage.getItem('Name');
console.log('retrieved Data!');
return d;


const [name, setName] = useState(()=>0);
  useEffect(() => 
    getData().then(setName);
    console.log('moving on...');
  , []);

但执行顺序是:

“继续……” '检索数据!'

应该是相反的地方

【问题讨论】:

响应编辑,'moving on'首先解析的原因是因为getData是一个异步函数。这意味着程序在移动到下一行之前不会等待 getData 完成执行。这可以。这只是意味着组件将在没有设置名称的情况下“渲染”一次,然后在调用 setName 时再次重新渲染,此时“名称”变量将填充来自 asyncstorage 的任何内容。在这种情况下,不需要在 useState() 中放置回调函数。将其初始化为一些文字就可以了。 【参考方案1】:

您会将您的状态初始化为某个空值。也许是一个空字符串,也许是一个空值。如果你愿意,你可以让你的组件在这段时间内渲染一些不同的东西,比如加载指示器。然后一旦数据加载,你可以设置状态再次渲染。

export default function App() 
  const [name, setName] = useState(null);
  useEffect(() => 
    getData().then(value => setName(value));
  , []);

  if (name === null) 
    return <Text>Loading...</Text>
   else 
    return (<View>...</View>)
  

【讨论】:

getData().then(setName); :) 真的很抱歉我忘了说我试过这个但我仍然没有得到想要的效果,你可以在问题的编辑中看到 @cakelover 您的编辑显示getData().then(setName(value));,这是错误的。它立即设置状态,并将结果传递给.then。将其更改为 getData().then(value =&gt; setName(value));getData().then(setName); 哦,我真的很抱歉我在我的实际代码中写了getData().then(setName);,我在编辑时不小心写错了,我现在修好了【参考方案2】:

你必须使用useEffect

export default function App() 
  const [name, setName] = useState('');

  useEffect(() => 
    const bootstrap = async () => 
      const name = await getdata();
      setName(name);
    ;
    bootstrap();
  , []);
  return <View>...</View>;

【讨论】:

我尝试了这种方法,但它没有给出想要的结果,很抱歉我忘记在问题中提及它,我现在已经进行了编辑 @cakelover 有什么问题? 执行顺序,你可以在下面的编辑中看到我提到它应该是相反的方式,即 getdata 然后继续【参考方案3】:

我认为这里最好的解决方案是将 name 初始化为 false,然后在 .then() 回调中使用 'setName'。 setName(以及那些通常来自 useState 的 setter 函数)旨在作为一种异步更新该值的方式。

我在这里将名称设置为布尔值“假”并严格检查。如果存在该名称字段为空的情况,则可以将其设置为空字符串而不会导致无限循环。

async function getdata()
let d= await AsyncStorage.getItem('Name');
return d;


export default function App() 
 const [name, setName] = useState(false);

 useEffect(() => 
  if (name === false) 
   getData().then((name) => 
    setName(name);
   );
  
 , []);
 
 return (<View>...</View>)

请记住,您的视图必须能够处理错误的名称值。如何做到这一点取决于您。

^^^上述解决方案应该可以有效地解决您的问题,但我会在这里快速说明:如果您的组件相对复杂,它可能会由于其他原因重新渲染多次,因此会运行 if( name === false) 条件,因为它等待回调解决。这不会破坏事情,但它会对异步存储造成不必要的影响,并可能影响性能。如果您发现是这种情况,请使用下面的有用文章查看“去抖动”回调。 https://medium.com/@gabrielmickey28/using-debounce-with-react-components-f988c28f52c1

【讨论】:

不好意思我有点慢,我还是不明白if (name === false)的用法是什么?因为我们事先将它初始化为假,所以它不总是真的吗?您是否建议将其作为缓冲区,例如在获取值时添加“加载”文本? 它允许您避免在每次重新渲染时从异步存储重新加载名称,因为“false”实际上是一个表示“尚未加载”的标志。在最初从存储中获取名称后,它会被拉入您的组件中,不需要再次获取。 我不确定,但 useState(()=&gt;false) 不能避免每次都渲染吗? useState 仅在第一次渲染时初始化值。随后的渲染周期,例如当 getData 的回调解决时由 setName 触发的渲染周期,不会将“名称”重新初始化为 false。

以上是关于从 useState 中的异步函数获取值的主要内容,如果未能解决你的问题,请参考以下文章

useState & useReducer

useReducer 和 useState

hooks中,useState异步问题解决方案

React hooks:为啥异步函数中的多个 useState 设置器会导致多次重新渲染?

react中的useState与setState的异步问题

useState 与异步函数返回 Promise <pending>