如何操作上下文-将函数附加到上下文或将派发包装在钩子中?

Dac*_*d3r 9 reactjs react-context react-hooks

我想知道推荐的最佳实践是处理和公开新的React Context。

操纵上下文状态的最简单方法似乎是将函数附加到上下文,该函数一旦被调用就可以调度(usereducer)或setstate(useState)来更改其内部值。

export const TodosProvider: React.FC<any> = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, null, init);

  return (
    <Context.Provider
      value={{
        todos: state.todos,
        fetchTodos: async id => {
          const todos = await getTodos(id);
          console.log(id);
          dispatch({ type: "SET_TODOS", payload: todos });
        }
      }}
    >
      {children}
    </Context.Provider>
  );
};

export const Todos = id => {
  const { todos, fetchTodos } = useContext(Context);
  useEffect(() => {
    if (fetchTodos) fetchTodos(id);
  }, [fetchTodos]);
  return (
    <div>
      <pre>{JSON.stringify(todos)}</pre>
    </div>
  );
};
Run Code Online (Sandbox Code Playgroud)

但是,有人告诉我直接暴露和使用react上下文对象可能不是一个好主意,而是被告知将其包装在一个钩子中。

export const TodosProvider: React.FC<any> = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, null, init);

  return (
    <Context.Provider
      value={{
        dispatch,
        state
      }}
    >
      {children}
    </Context.Provider>
  );
};

const useTodos = () => {
  const { state, dispatch } = useContext(Context);
  const [actionCreators, setActionCreators] = useState(null);

  useEffect(() => {
    setActionCreators({
      fetchTodos: async id => {
        const todos = await getTodos(id);
        console.log(id);
        dispatch({ type: "SET_TODOS", payload: todos });
      }
    });
  }, []);

  return {
    ...state,
    ...actionCreators
  };
};

export const Todos = ({ id }) => {
  const { todos, fetchTodos } = useTodos();
  useEffect(() => {
    if (fetchTodos && id) fetchTodos(id);
  }, [fetchTodos]);

  return (
    <div>
      <pre>{JSON.stringify(todos)}</pre>
    </div>
  );
};
Run Code Online (Sandbox Code Playgroud)

我在这里为这两种变体都制作了运行代码示例:https : //codesandbox.io/s/mzxrjz0v78?fontsize=14

因此,现在我对这两种方法中的哪一种是正确的方法感到有些困惑?

Shu*_*tri 6

useContext直接在组件中使用绝对没有问题。但是,它迫使必须使用上下文值的组件知道要使用的上下文。

如果您在应用程序中有多个要使用TodoProvider上下文的组件,或者您的应用程序中有多个上下文,则可以使用自定义钩子对其进行一些简化

使用上下文时,还必须考虑的另一件事是,不应在每个渲染器上创建一个新对象,否则context即使没有任何更改,所有正在使用的组件也会重新渲染。为此,您可以使用useMemo挂钩

const Context = React.createContext<{ todos: any; fetchTodos: any }>(undefined);

export const TodosProvider: React.FC<any> = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, null, init);
  const context = useMemo(() => {
    return {
      todos: state.todos,
      fetchTodos: async id => {
        const todos = await getTodos(id);
        console.log(id);
        dispatch({ type: "SET_TODOS", payload: todos });
      }
    };
  }, [state.todos, getTodos]);
  return <Context.Provider value={context}>{children}</Context.Provider>;
};

const getTodos = async id => {
  console.log(id);
  const response = await fetch(
    "https://jsonplaceholder.typicode.com/todos/" + id
  );
  return await response.json();
};
export const useTodos = () => {
  const todoContext = useContext(Context);
  return todoContext;
};
export const Todos = ({ id }) => {
  const { todos, fetchTodos } = useTodos();
  useEffect(() => {
    if (fetchTodos) fetchTodos(id);
  }, [id]);
  return (
    <div>
      <pre>{JSON.stringify(todos)}</pre>
    </div>
  );
};
Run Code Online (Sandbox Code Playgroud)

工作演示

编辑:

由于getTodos只是一个无法更改的函数,因此将其用作update参数useMemo是否有意义?

getTodos如果getTodos方法正在更改并且在功能组件内调用,则在useMemo中将其传递给依赖项数组是有意义的。通常,您会记住使用的方法,useCallback这样它就不会在每个渲染器上创建,而是仅在其来自封闭范围的依赖关系发生更改以更新其词法范围内的依赖关系时才创建。现在,在这种情况下,您需要将其作为参数传递给依赖项数组。

但是,您可以忽略它。

另外,您将如何处理初始效果。假设在安装提供程序时是否要在useEffect挂钩中调用“ getTodos”?您能也记住那个电话吗?

您将在Provider中在初始安装时简单地产生影响

export const TodosProvider: React.FC<any> = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, null, init);
  const context = useMemo(() => {
    return {
      todos: state.todos,
      fetchTodos: async id => {
        const todos = await getTodos(id);
        console.log(id);
        dispatch({ type: "SET_TODOS", payload: todos });
      }
    };
  }, [state.todos]);
  useEffect(() => {
      getTodos();
  }, [])
  return <Context.Provider value={context}>{children}</Context.Provider>;
};
Run Code Online (Sandbox Code Playgroud)