ReactJS Hooks 依赖一个异步操作
ReactJS Hooks dependency an async operation
所以我有一个挂载的钩子,它从 indexedDB 读取数据并将该数据存储在其内部状态
我遇到的问题是 indexedDB 数据从另一个组件 (added/removed) 更改,我需要在此挂钩中对这些更改做出反应。我不是 100% 熟悉钩子以及如何完成它,但首先想到的是将来自 indexedDB 的值作为钩子依赖项。
但是,从 indexedDB 读取数据是一个异步操作,挂钩依赖项将是一个 .. promise。
基本上,流程如下:
- 组件 1 像这样调用挂钩:
const eventItems = useEventListItems({
sortBy,
sortGroupedBy,
eventTimestamp,
events,
assets,
touchedEventIds,
unsyncedEvents, // <--- this is the one that we need
order,
});
useEventListItems
挂钩在装载时从索引数据库中读取数据,将其存储在其内部状态中,returns 它:
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 });
indexedDB 的 unsyncedEvents 然后从另一个组件更改。
现在应该发生的是,useEventListItems
挂钩应该监听IDB中的变化并更新其内部状态的unsyncedEvents ,将它们传递给使用此挂钩的组件。我将如何实现这一目标?
我的第一个想法是在 useEventListItems 挂钩中有这样的东西:
useEffect(() => {
setUnsyncedEvents(..newValueFromIdb);
}, [ await readUnsyncedEvents()]);
但这行不通,因为这是一个承诺。无论如何,我可以拥有一个异步操作返回的值作为钩子依赖吗?
您可以使用 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>
)
}
所以我有一个挂载的钩子,它从 indexedDB 读取数据并将该数据存储在其内部状态
我遇到的问题是 indexedDB 数据从另一个组件 (added/removed) 更改,我需要在此挂钩中对这些更改做出反应。我不是 100% 熟悉钩子以及如何完成它,但首先想到的是将来自 indexedDB 的值作为钩子依赖项。
但是,从 indexedDB 读取数据是一个异步操作,挂钩依赖项将是一个 .. promise。
基本上,流程如下:
- 组件 1 像这样调用挂钩:
const eventItems = useEventListItems({
sortBy,
sortGroupedBy,
eventTimestamp,
events,
assets,
touchedEventIds,
unsyncedEvents, // <--- this is the one that we need
order,
});
useEventListItems
挂钩在装载时从索引数据库中读取数据,将其存储在其内部状态中,returns 它:
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 });
indexedDB 的 unsyncedEvents 然后从另一个组件更改。
现在应该发生的是,
useEventListItems
挂钩应该监听IDB中的变化并更新其内部状态的unsyncedEvents ,将它们传递给使用此挂钩的组件。我将如何实现这一目标?
我的第一个想法是在 useEventListItems 挂钩中有这样的东西:
useEffect(() => {
setUnsyncedEvents(..newValueFromIdb);
}, [ await readUnsyncedEvents()]);
但这行不通,因为这是一个承诺。无论如何,我可以拥有一个异步操作返回的值作为钩子依赖吗?
您可以使用 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>
)
}