所以我阅读了这些关于使用自定义钩子获取数据的博客文章,例如我们有一个自定义钩子执行 API 调用、设置数据、可能的错误以及旋转 isFetching 布尔值:
export const useFetchTodos = () => {
const [data, setData] = useState();
const [isFetching, setIsFetching] = useState(false);
const [error, setError] = useState();
useEffect(() => {
setIsFetching(true);
axios.get('api/todos')
.then(response => setData(response.data)
.catch(error => setError(error.response.data)
.finally(() => setFetching(false);
}, []);
return {data, isFetching, error};
}
然后在我们组件的顶层,我们只需调用 const { data, error, fetching } = useFetchTodos();
,一切都很好,我们渲染我们的组件并获取所有待办事项。
我不明白的是,我们如何根据组件的内部状态向钩子发送动态数据/参数,同时又不违反钩子的规则?
例如,假设我们有一个 useFetchTodoById(id)
钩子的定义方式与上述相同,我们将如何传递 id
?假设我们呈现 Todos 的 TodoList
组件如下所示:
export const TodoList = (props) => {
const [selectedTodo, setSelectedTodo] = useState();
useEffect(() => {
useFetchTodoById(selectedTodo.id) --> INVALID HOOK CALL, cannot call custom hooks from useEffect,
and also need to call our custom hooks at the "top level" of our component
}, [selectedTodo]);
return (<ul>{props.todos.map(todo => (
<li onClick={() => setSelectedTodo(todo.id)}>{todo.name}</li>)}
</ul>);
}
我知道对于这个特定用例,我们可以通过 props 传递我们的 selectedTodo
并在我们的组件顶部调用我们的 useFetchTodoById(props.selectedTodo.id)
,但我只是说明了我遇到的这种模式的问题,我们不会总是有机会在道具中接收我们需要的动态数据。
此外——我们如何将这种模式应用于采用动态数据属性的 POST/PUT/PATCH
请求?
答案 0 :(得分:2)
你应该有一个基本的 useFetch
钩子,它接受一个 url,并在 url 更改时获取:
const useFetch = (url) => {
const [data, setData] = useState();
const [isFetching, setIsFetching] = useState(false);
const [error, setError] = useState();
useEffect(() => {
if(!url) return;
setIsFetching(true);
axios.get(url)
.then(response => setData(response.data))
.catch(error => setError(error.response.data))
.finally(() => setFetching(false));
}, [url]);
return { data, isFetching, error };
};
现在您可以从这个基本钩子创建其他自定义钩子:
const useFetchTodos = () => useFetch('api/todos');
你还可以让它响应动态变化:
const useFetchTodoById = id => useFetch(`api/todos/${id}`);
并且您可以在组件中使用它,而无需将其包装在 useEffect
中:
export const TodoList = (props) => {
const [selectedTodo, setSelectedTodo] = useState();
const { data, isFetching, error } = useFetchTodoById(selectedTodo.id);
return (
<ul>{props.todos.map(todo => (
<li onClick={() => setSelectedTodo(todo.id)}>{todo.name}</li>)}
</ul>
);
};