【问题标题】:React custom hooks taking dynamic data properties采用动态数据属性的 React 自定义钩子
【发布时间】:2021-06-24 00:25:12
【问题描述】:

所以我已经阅读了这些关于使用自定义挂钩来获取数据的博客文章,例如,我们有一个自定义挂钩来执行 API 调用、设置数据、可能的错误以及 spinny 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?假设我们的 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>);
}

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

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

【问题讨论】:

    标签: reactjs rest react-hooks


    【解决方案1】:

    你应该有一个基本的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>
      );
    };
    

    【讨论】:

    • 啊,所以我的误解是我认为我需要依靠我的 useEffect 来跟踪状态变化。另一个问题,你将如何处理向 api 发布新数据?例如useCreateTodo(newTodo) - 我们不能只在组件顶部调用它,因为我们的newTodo 状态将在挂载时未定义,并且将自定义钩子包装在 if 中是无效的。那么唯一的其他选择是将 if undefined 检查包装在钩子本身的主体内?
    • 如果没有url,你应该避免改变状态,调用API。在useEffect 中检查url 是否不存在!url,如果是则返回。查看更新的答案。
    • 嗯,同样的概念也适用于我的useCreateTodo。谢谢,你帮了大忙!
    猜你喜欢
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 2020-08-27
    • 1970-01-01
    • 1970-01-01
    • 2020-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多