反应本机:useState 未正确更新

时间:2021-05-15 22:01:24

标签: reactjs react-native use-effect use-state

我是本机反应的新手,目前正在为无限滚动列表视图而苦苦挣扎。这是一个日历列表,需要根据所选公司(作为道具提供)进行更改。问题是:prop(还有 myCompany 状态发生了变化,但在 _loadMoreAsync 方法中,prop.companymyCompany 都保持它们的初始值。

import * as React from 'react';
import { FlatList } from 'react-native';
import * as Api from '../api/api';
import InfiniteScrollView from 'react-native-infinite-scroll-view';

function CalenderFlatList(props: { company: any }) {
  const [myCompany, setMyCompany] = React.useState(null);
  const [data, setData] = React.useState([]);
  const [canLoadMore, setCanLoadMore] = React.useState(true);
  const [startDate, setStartDate] = React.useState(undefined);

  let loading = false;

  React.useEffect(() => {
    setMyCompany(props.company);
  }, [props.company]);

  React.useEffect(() => {
    console.log('set myCompany to ' + (myCompany ? myCompany.name : 'undefined'));
    _loadMoreAsync();
  }, [myCompany]);

  async function _loadMoreAsync() {
    if ( loading )
      return;

    loading = true;

    if ( myCompany == null ) {
      console.log('no company selected!');
      return;
    } else {
      console.log('use company: ' + myCompany.name);
    }

    Api.fetchCalendar(myCompany, startDate).then((result: any) => {
      // code is a little more complex here to keep the already fetched entries in the list...
      setData(result);

      // to above code also calculates the last day +1 for the next call
      setStartDate(lastDayPlusOne);

      loading = false;
    });
  }

  const renderItem = ({ item }) => {
    // code to render the item
  }

  return (
    <FlatList
      data={data}
      renderScrollComponent={props => <InfiniteScrollView {...props} />}
      renderItem={renderItem}
      keyExtractor={(item: any) => '' + item.uid }
      canLoadMore={canLoadMore}
      onLoadMoreAsync={() => _loadMoreAsync() }
    />
  );
}

我在这里不明白的是,为什么 myCompany_loadMoreAsync 中根本没有更新,而 startDate 正确更新并准确加载日历的下一个条目。

在道具 company 更改后,我希望得到以下输出:

<块引用>

将我的公司设置为公司名称

使用公司公司名称

但我得到:

<块引用>

将我的公司设置为公司名称

未选择任何公司!

我尝试稍微减少代码以将其分解为最重要的部分。对此有什么建议吗?

3 个答案:

答案 0 :(得分:0)

Google for use Effect staleclosure.

当从 useEffect 调用该函数时,它是从过时的上下文中调用的 - 这显然是一个 javascript 功能:) 所以基本上你所经历的行为是预期的,你需要找到一种方法来解决它。

一种方法可能是向您从 useEffect 传递的 _loadMoreAsync 添加一个(可选)参数。如果此参数未定义(从其他地方调用时将使用),则使用 state 中的值。

答案 1 :(得分:0)

试试

<FlatList
    data={data}
    renderScrollComponent={props => <InfiniteScrollView {...props} />}
    renderItem={renderItem}
    keyExtractor={(item: any) => '' + item.uid }
    canLoadMore={canLoadMore}
    onLoadMoreAsync={() => _loadMoreAsync() }
    extraData={myCompany}
 />

如果您的 FlatList 依赖于一个状态变量,您需要将该变量传递给 extraData 道具以触发您的列表的重新渲染。更多信息here

答案 2 :(得分:0)

在这个问题上睡了两个晚上后,我自己解决了。原因是另一段使用 React.useCallback() 的代码的影响。并且由于“useCallback 将返回回调的记忆版本,该版本仅在依赖项之一发生更改时才会更改”(https://reactjs.org/docs/hooks-reference.html#usecallback),因此代码使用变量的旧(或初始)状态。

从头开始创建新的整个页面后,我发现这就是这种行为的原因。