ReactJS Зависимость хуков в асинхронной c операции - PullRequest
0 голосов
/ 10 июля 2020

Итак, у меня есть перехватчик, который при монтировании считывает данные из indexedDB и сохраняет эти данные во внутреннем состоянии

Проблема, с которой я столкнулся, заключается в том, что данные indexedDB изменяются из другого компонента (добавлено / удалено), и мне нужно отреагировать на эти изменения в этом хуке. Я не на 100% знаком с хуками и тем, как это можно сделать, но первая мысль была бы иметь в качестве зависимости хука значение из indexedDB.

ОДНАКО, чтение данных из indexedDB есть операция asyn c и зависимость от ловушки будет ... обещанием.

Итак, в основном, поток выглядит следующим образом:

  1. Компонент 1 вызывает ловушку следующим образом:
  const eventItems = useEventListItems({
    sortBy,
    sortGroupedBy,
    eventTimestamp,
    events,
    assets,
    touchedEventIds,
    unsyncedEvents, // <--- this is the one that we need
    order,
  });
Ловушка 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 });

unsyncedEvents из indexedDB затем заменяется другим компонентом.

Что должно сейчас произойти, так это то, что хук useEventListItems должен прослушивать к изменениям в IDB и обновите unsyncedEvents в его внутреннем состоянии, передав их компоненту, который использует эту ловушку. Как мне этого добиться?

Моя первая мысль заключалась в том, чтобы иметь что-то подобное в хуке useEventListItems:

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

но это не сработает, так как это ' будет обещанием. Могу ли я каким-то образом иметь в качестве зависимости от ловушки значение, возвращаемое операцией asyn c?

1 Ответ

1 голос
/ 10 июля 2020

Вы можете использовать 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>
  )
}
...