【问题标题】:ReactJS Hooks dependency an async operationReactJS Hooks 依赖一个异步操作
【发布时间】:2020-10-30 23:19:33
【问题描述】:

所以我有一个挂载的钩子,从 indexedDB 读取数据并将该数据存储在其内部状态

我遇到的问题是 indexedDB 数据从另一个组件(添加/删除)更改,我需要对这个钩子中的这些更改做出反应。我不是 100% 熟悉钩子以及如何做到这一点,但首先想到的是将 indexedDB 中的值作为钩子依赖。

但是,从 indexedDB 中读取数据是一个异步操作,并且钩子依赖是一个.. 承诺。

所以基本上,流程如下:

  1. 组件 1 像这样调用钩子:
  const eventItems = useEventListItems({
    sortBy,
    sortGroupedBy,
    eventTimestamp,
    events,
    assets,
    touchedEventIds,
    unsyncedEvents, // <--- this is the one that we need
    order,
  });
  1. useEventListItems 挂钩在挂载时从索引数据库中读取数据,将其存储在其内部状态中并返回:
  const { readUnsyncedEvents } = useDebriefStore();
  const [unsyncedEvents, setUnsyncedEvents] = useState<number[]>([]);
  useEffectAsync(async () => {
    const storedUnsyncedEventIds = await readUnsyncedEvents<number[]>();
    if (storedUnsyncedEventIds?.data) {
      setUnsyncedEvents(storedUnsyncedEventIds.data);
    }
  }, [setUnsyncedEvents]);

readUnsyncedEvents 在哪里:

export const readUnsyncedEvents = <T>(type: Type): Promise<DebriefStoreEntry<T>> =>
  debriefStore
    .get(type)
    .then((entry) => entry && { data: entry.data, timestamp: entry.timestamp });
  1. indexedDB 中的 unsyncedEvents 然后从另一个组件中更改。

  2. 应该现在发生的是,useEventListItems 钩子应该监听 IDB 中的变化并更新其内部状态的 unsyncedEvents,将它们传递给使用该钩子的组件。我将如何实现这一目标?

我的第一个想法是在 useEventListItems 挂钩中有这样的东西:

useEffect(() => {
  setUnsyncedEvents(..newValueFromIdb);
}, [ await readUnsyncedEvents()]);

但这行不通,因为这将是一个承诺。无论如何我可以有一个钩子依赖,一个异步操作返回的值?

【问题讨论】:

    标签: javascript reactjs react-hooks indexeddb


    【解决方案1】:

    您可以使用 Context API 从 IDB 重新获取数据。

    这里的想法是创建一个带有计数器变量的上下文,该变量将在每次 IDB 更新操作后更新。而useEventListItems 钩子将从上下文中读取该计数器变量并触发useEffect 钩子。

    export const IDBContext = React.createContext({
      readFromIDB: null,
      setReadFromIDB: () => {}
    });
    
    export const IDBContextProvider = ({ children }) => {
      const [readFromIDB, setReadFromIDB] = useState(0);
      return (
        <IDBContext.Provider value={{ readFromIDB, setReadFromIDB }}>
          {children}
        </IDBContext.Provider>
      );
    };  
    

    这就是你的useEventListItems 的样子。

    const { readUnsyncedEvents } = useDebriefStore();
    const [unsyncedEvents, setUnsyncedEvents] = useState<number[]>([]);
    
    const {readFromIDB} = useContext(IDBContext); // this variable will be updated after each IDB update.
    
    useEffectAsync(async () => {
      const storedUnsyncedEventIds = await readUnsyncedEvents<number[]>();
      if (storedUnsyncedEventIds?.data) {
        setUnsyncedEvents(storedUnsyncedEventIds.data);
      }
    }, [readFromIDB,setUnsyncedEvents]); // added that to dependency array to trigger the hook on value change.
    

    以下是组件:

    const IDBUpdateComponent = ()=>{
    const {readFromIDB,setReadFromIDB} = useContext(IDBContext);
      const updateIDB = ()=>{
        someIDBUpdateOpetation().then(res=>{
          setReadFromIDB(readFromIDB+1) // update the context after IDB update is successful.
        }).catch(e=>{})
      }
    
      return(
        <div>IDBUpdateComponent</div>
      );
    }
    
    const IDBConsumerComponent = ()=>{
      return (
        <div>IDBConsumerComponent</div>
      )
    }
    

    只需确保两个组件都包装在上下文中,以便它们可以访问值。

    const App = ()=>{
      return(
        <div>
          <IDBContextProvider>
            <IDBUpdateComponent />
            <IDBConsumerComponent />
          </IDBContextProvider>
        </div>
      )
    }
    

    【讨论】:

    • 这对我有所帮助 - 为我指明了正确的方向。代码库在同步方面存在重大问题,因此我需要解决一些去抖动的保存功能和承诺,但如果没有您的解决方案 Murli,我将无法解决它!谢谢。
    猜你喜欢
    • 2020-02-16
    • 2020-04-23
    • 1970-01-01
    • 2016-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多