React 自定义挂钩采用动态数据属性

Ogn*_*šić 7 rest reactjs react-hooks

我读过这些关于使用自定义钩子来获取数据的博客文章,例如我们有一个自定义钩子执行 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};
}
Run Code Online (Sandbox Code Playgroud)

然后,在组件的顶层,我们只需调用即可const { data, error, fetching } = useFetchTodos();,非常好,我们使用获取的所有待办事项来渲染我们的组件。

我不明白的是,我们如何根据组件的内部状态向钩子发送动态数据/参数,而不违反钩子规则?

例如,假设我们有useFetchTodoById(id)一个与上面的钩子定义方式相同的钩子,我们将如何传递它id?假设TodoList呈现 Todos 的组件如下:

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>);
}
Run Code Online (Sandbox Code Playgroud)

我知道对于这个特定的用例,我们可以传递我们的selectedTodothrough props 并useFetchTodoById(props.selectedTodo.id)在组件的顶部调用我们的,但我只是说明我遇到的这种模式的问题,我们并不总是有接收动态数据的奢侈我们在道具中需要的。

另外,我们如何将这种模式应用于POST/PUT/PATCH采用动态数据属性的请求?

Ori*_*ori 6

您应该有一个基本的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 };
};
Run Code Online (Sandbox Code Playgroud)

现在您可以从此基本挂钩创建其他自定义挂钩:

const useFetchTodos = () => useFetch('api/todos');
Run Code Online (Sandbox Code Playgroud)

你还可以让它响应动态变化:

const useFetchTodoById = id => useFetch(`api/todos/${id}`);
Run Code Online (Sandbox Code Playgroud)

您可以在组件中使用它,而无需将其包装在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>
  );
};
Run Code Online (Sandbox Code Playgroud)