异步确实等待数据在 redux-thunk 函数中返回

Async does wait for data to be returned in a redux-thunk function

我一直在尝试用来自我的 mongo-db 领域数据库的数据填充我的 redux 存储。 每当我 运行 下面的函数都会执行得很好,但问题是数据会延迟,最终不会到达我的 redux 存储。

我的 thunk 函数:

export const addItemsTest = createAsyncThunk(
  "addItems",
  async (config: any) => {
    try {
      return await Realm.open(config).then(async (projectRealm) => {
        let syncItems = await projectRealm.objects("Item");
        await syncItems.addListener((x, changes) => {
          x.map(async (b) => {
            console.log(b);
            return b;
          });
        });
      });
    } catch (error) {
      console.log(error);
      throw error;
    }
  }
);

和我的 redux 减速器:

  extraReducers: (builder) => {
      builder.addCase(addItemsTest.fulfilled, (state, { payload }: any) => {
        try {
          console.log("from add Items");
          console.log(payload);
          state.push(payload);
        } catch (error) {
            console.log(error)
         }
      });
  }

预期结果: 我的 redux 商店应该有这些数据一次 addItemsTest return something:

[{
itemCode: 1,
itemDescription: 'Soccer Ball',
itemPrice: '35',
partition: 'partitionValue',
},
{
itemCode: 2,
itemDescription: 'Base Ball',
itemPrice: '60',
partition: 'partitionValue',
}
]

实际结果:

混合语法

您正在以一种非常混乱的方式组合 await/asyncPromise.then() 语法。混合使用这两种语法不是错误,但我不推荐这样做。坚持 await/async

无效Return

您的操作实际上 return 现在没有任何价值,因为您的内部 then 函数没有 return 任何东西。唯一的 return 位于 then 内部,位于 x.map 回调中。 await syncItems 是映射器 的 returned 值 ,不适用于您的函数。

现在,这是你的 thunk 所做的:

  • 打开一个连接
  • 从领域获取物品
  • 为那些记录更改的项目添加侦听器
  • returns 一个 Promise 解析为 void

解决方案

我相信你想要的是这个:

export const addItemsTest = createAsyncThunk(
  "addItems",
  async (config: any) => {
    try {
      const projectRealm = await Realm.open(config);
      const syncItems = await projectRealm.objects("Item");
      console.log(syncItems);
      return syncItems;
    } catch (error) {
      console.log(error);
      throw error;
    }
  }
);

没有日志记录,可以简化为:

export const addItemsTest = createAsyncThunk(
  "addItems",
  async (config: any) => {
    const projectRealm = await Realm.open(config);
    return await projectRealm.objects("Item");
  }
);

您不需要 catch 错误,因为 createAsyncThunk 将通过调度错误操作来处理错误。

编辑:监听更改

您的意图似乎是将您的 redux 存储与您的 Realm collection 中的更改同步。因此,您想向调用 dispatch 的 collection 添加一个侦听器,并执行一些操作来处理更改。

这里我假设此操作采用一个数组,其中包含您 collection 中的所有项目。像这样:

const processItems = createAction("processItems", (items: Item[]) => ({
  payload: items
}));

替换您所在州的整个数组是最简单的方法。当你用相同的版本替换项目 object 时,它会导致一些不必要的 re-renders,但这不是什么大问题。

或者,您可以传递 changes 的特定属性,例如 insertions 并在 case-by-case 基础上在您的减速器中处理它们。

为了添加调度 processItems 的侦听器,我们需要访问两个变量:领域 config 和 redux dispatch。您可以在您的组件中或通过调用“init”操作来执行此操作。我不认为真的有太大区别。如果你愿意,你可以在你的减速器中做一些事情来响应“init”动作。

这是一个添加监听器的函数。 Realm.Results object 是“array-like”,但不完全是一个数组,因此我们使用 [...x] 将其转换为数组。

仅供参考,此函数可能会引发错误。如果在 createAsyncThunk 中使用这很好,但在组件中我们希望 catch 那些错误。

const loadCollection = async (config: Realm.Configuration, dispatch: Dispatch): Promise<void> => {
  const projectRealm = await Realm.open(config);
  const collection = await projectRealm.objects<Item>("Item");
  collection.addListener((x, changes) => {
    dispatch(processItems([...x]));
  });
}

通过中间 addListener 动作创建者添加侦听器:

export const addListener = createAsyncThunk(
  "init",
  async (config: Realm.Configuration, { dispatch }) => {
    return await loadCollection(config, dispatch);
  }
);

// is config a prop or an imported global variable?
const InitComponent = ({config}: {config: Realm.Configuration}) => {
  const dispatch = useDispatch();

  useEffect( () => {
    dispatch(addListener(config));
  }, [config, dispatch]);

  /* ... */
}

直接添加监听器:

const EffectComponent = ({config}: {config: Realm.Configuration}) => {
  const dispatch = useDispatch();

  useEffect( () => {
    // async action in a useEffect need to be defined and then called
    const addListener = async () => {
      try {
        loadCollection(config, dispatch);
      } catch (e) {
        console.error(e);
      }
    }

    addListener();
    
  }, [config, dispatch]);

  /* ... */
}