我有一个反应挂钩useDbReadTable
,用于从接受tablename
和query
的初始数据的数据库中读取数据。它返回一个对象,除了来自数据库的数据外,该对象还包括isLoading
状态。
我想将此钩子包装在一个新的钩子中,该钩子接受{ tablename, query }
数组的初始数据,并返回一个对象,该对象具有数据库中每个表的数据,但isLoading
状态根据以下内容合并为一个布尔值:我的新钩子中的逻辑。
这个想法是,新钩子的调用者可以从多个表中请求数据,但只需要检查一个状态值即可。
我的想法是让新的钩子看起来像
编辑:更新的代码(我粘贴了错误的版本)
export const useDbRead = tableReads => {
let myState = {};
for (let i = 0; i < tableReads.length; ++i) {
const { tablename, query = {} } = tableReads[i];
const [{ isLoading, isDbError, dbError, data }] = useDbReadTable(tablename, query);
myState = { ...myState, [tablename]: { isLoading, isDbError, dbError, data }};
}
const finalState = {
...myState,
isLoading: Object.values(myState).reduce((acc, t) => acc || t.isLoading, false),
};
return [finalState];
};
但是,eslint在我的
useDbReadTable
调用中给了我这个错误:React Hook“ useDbReadTable”可以执行多次。可能是因为它是在循环中调用的。在每个组件渲染中,必须以完全相同的顺序调用React Hook。反应挂钩/挂钩规则
然后Rules for Hooks说,
仅顶层呼叫挂钩
不要在循环,条件或嵌套函数中调用Hook。相反,请始终在您的React函数的顶层使用挂钩。通过遵循此规则,可以确保每次渲染组件时都以相同的顺序调用Hook。这就是让React在多个useState和useEffect调用之间正确保留Hook的状态的原因。 (如果您感到好奇,我们将在下面对此进行深入说明。)
阅读规则和说明后,似乎唯一的问题是确保在所有重新渲染时都以相同的顺序调用挂钩。只要我确保传入新钩子的表的列表永不改变,我的新钩子是否可以正常工作(如我的初始测试所示)?还是我错过了什么?
更重要的是,有没有更好的主意如何实现这一点而不违反钩子规则?
Edit2:如果有帮助,请参见
useDbReadTable
。请注意,它包含的功能比我在问题中提到的功能还要多,因为我想使问题尽可能简单。我的问题是我的useDbRead
是一个很好的解决方案,还是在不违反钩子规则的情况下有一个好的方法?export const useDbReadTable = (initialTableName, initialQuery = {}, initialData = []) => {
const dbChangeFlag = useSelector(({appState}) => appState.dbChangeFlag);
const [tableName, setTableName] = useState(initialTableName);
const [query, setQuery] = useState(initialQuery);
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
isDbError: false,
dbError: {},
data: initialData,
});
useEffect(() => {
let didCancel = false;
const fetchData = async () => {
dispatch({ type: dataFetch.FETCH_INIT });
try {
const result = Array.isArray(query) ?
await db[tableName].batchGet(query) // query is an array of Ids
:
await db[tableName].find(query);
if (!didCancel) {
dispatch({ type: dataFetch.FETCH_SUCCESS, payload: result });
}
} catch (error) {
if (!didCancel) {
dispatch({ type: dataFetch.FETCH_FAILURE, payload: error });
}
}
};
fetchData().then(); // .then() gets rid of eslint warning
return () => {
didCancel = true;
};
}, [query, tableName, dbChangeFlag]);
return [state, setQuery, setTableName];
};
最佳答案
通过使useDbReadSingle
本身可以识别数组,可以避免使用useDbRead
。就像是:
export const useDbRead = tableReads => {
const [loading, setLoading] = useState(true);
useEffect(() => {
const doIt = async () => {
// you would also need to handle the error case, but you get the idea
const data = await Promise.all(
tableReads.map(tr => {
return mydbfn(tr);
})
);
setLoading(false);
};
doIt();
}, [tableReads]);
return { loading, data };
};
当需要将其用于单个表读取时,只需对具有单个元素的数组进行调用即可。
const {loading, data: [d]} = useDbRead([mytableread])
关于javascript - React钩子(Hook):用单个钩子(Hook)包装钩子(Hook)的多个实例的最佳实践是什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59460331/