+++ draft = true +++

Reference

Global state

  • Avoid prop drilling
  • Access data accoss our application without copying
flowchart A["PostsContext()"] B["posts state: [posts, setPosts]"] C["status state: [status, setStatus]"] D["PostsContext()"] E["refetch function hook"] A--create-->B C--refer-->B D--refer-->B

Global components: Not remove dedup requests

export function PostsContext({ children }) {
  // Every time call `PostsContext` function, we will create bellow objects
  const [posts, setPosts] = React.useState([]);
  const [error, setError] = React.useState();
  const [status, setStatus] = React.useState("loading");

  const refetch = async () => {
    try {
      setStatus("loading");
      const posts = await axios.get("/api/posts").then((res) => res.data);
      setPosts(posts);
      setStatus("success");
    } catch (err) {
      setstatus("error");
      setError(err);
    }
  };

  const contextValue = React.useMemo(() => ({
    posts,
    status,
    error,
    refetch,
  }));

  return <context.Provider value={contextValue}>{children}</context.Provider>;
}
  • Consumer
export default usePosts() {

    const {posts, status, error, refetch} = React.useContext(context);

    // Every component use `usePosts` then `refetch` will be called at mounting time
    // Not remove dedup requesting
    React.useEffect(() => {
        refetch()
    }, []);

    return {
        posts,
        status,
        error,
        refetch,
    }

}

Fix dedup requests

export function PostsContext({ children }) {
  // Every time call `PostsContext` function, we will create bellow objects
  const [posts, setPosts] = React.useState([]);
  const [error, setError] = React.useState();
  const [status, setStatus] = React.useState("loading");

  // Add new ref object which cannot reset between re-rendering
  const activePromiseRef = React.useRef(false);

 // Stick `refetch` function with a new ref object
 // If any other requests that happen while the promise is still pending we will reuse the promise from the original request
  const refetch = () => {
    if (!activePromiseRef.current) {
      activePromiseRef.current = async () => {
        try {
          setStatus("loading");
          const posts = await axios.get("/api/posts").then((res) => res.data);
          setPosts(posts);
        } catch (err) {
          setStatus("error");
          setError(err);
        }
        finally {
            activePromiseRef.current = false;
        }
      }(); // Execute immediately
    }

    // If any other requests that happen while the promise is still pending we will reuse the promise from the original request (we don't fire extra one)
    return activePromiseRef.current;
  };



  const contextValue = React.useMemo(() => ({
    posts,
    status,
    error,
    refetch,
  }));

  return <context.Provider value={contextValue}>{children}</context.Provider>;
}