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

时间:2021-03-27 08:31:12

标签: reactjs rest 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};
}

然后在我们组件的顶层,我们只需调用 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 请求?

1 个答案:

答案 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>
  );
};