Home > Development Tools > composer > Detailed steps for encapsulating React Context Composer (share)

Detailed steps for encapsulating React Context Composer (share)

藏色散人
Release: 2021-12-20 13:57:25
forward
2186 people have browsed it

This article is written by composer Tutorial column to introduce to you how to encapsulate a React Context Composer step by step. I hope it will be helpful to friends in need!

How do I encapsulate a React Context Composer step by step?

Motivation

There are many state management solutions for React, such as Redux, Mobx, Recoil, etc. So far, I have only experienced Redux, and I think it is still a bit cumbersome. . Because I usually write a lot of Hooks, I prefer to use Context Provider with the useContext hook, which makes it easy to split and combine states. Here, we will not discuss the pros and cons of each state management solution, but focus on a multi-layer nesting problem encountered when using Context.

The picture below is some code extracted from a taro react hooks ts project I was writing recently. I split some global state (the purpose of splitting is to reduce unnecessary re-rendering) and then nested them. This way of writing reminds me of the feeling of being dominated by callback hell, which is very uncomfortable. Therefore, I thought of sealing a high-order component myself and "flattening" the structure in terms of writing.

<LoadingContext.Provider value={{ loading, setLoading }}>
  <UserDataContext.Provider value={{ name: "ascodelife", age: 25 }}>
    <ThemeContext.Provider value={"light"}>
    {/* ....more Providers as long as you want */}
    </ThemeContext.Provider>
  </UserDataContext.Provider>
</LoadingContext.Provider>
Copy after login

The easiest solution

Here, I quickly wrote the first solution, using reduceRight to complete the nesting of Provider.

The reason reduceRight is used here instead of reduce is that we are more accustomed to the writing order from the outer layer to the inner layer.

// ContextComposer.tsx
import React from &#39;react&#39;;
type IContextComposerProps = {
  contexts: { context: React.Context<any>; value: any }[];
};
const ContextComposer: React.FC<IContextComposerProps> = ({ contexts, children }) => {
  return (
    <>
      {contexts.reduceRight((child, parent) => {
        const { context, value } = parent;
        return <context.Provider value={value}>{child}</context.Provider>;
      }, children)}
    </>
  );
};
export default ContextComposer;
// App.tsx
<ContextComposer
  contexts={[
    { context: ThemeContext, value: "light" },
    { context: UserDataContext, value: { name: "ascodelife", age: 25 } },
    { context: LoadingContext, value: { loading, setLoading } },
  ]}>
    { children }
</ContextComposer>
Copy after login

After actual experience, I found that although it can be used, the development experience is a little bit worse. The problem is that the value passed when the component enters the parameter is of type any, which means that the static type check of ts is abandoned. When passing parameters, since static type checking will not be done on the value, not only will there not be any code prompts when typing the code, but it may also cause some relatively low-level runtime errors. Bad review!

Transformation plan based on React.cloneElement()

In order to transform the above scheme, I turned to a relatively unpopular but easy-to-use function——React. cloneElement(). There are not many points worth noting about this function. Mainly take a look at its three input parameters. The first is the parent element, the second is the parent props, and the third is the remaining parameters...children, except for the first parameter. Except for this, all other values ​​are optional.

For example:

<!-- 调用函数 -->
React.cloneElement(<div/>,{},<span/>);
<!-- 相当于创建了这样一个结构 -->
<div> 
    <span></span>
</div>
Copy after login

Then let’s start the transformation. The reduceRight frame remains unchanged. Change the input parameter type and reduceRight callback.

// ContextComposer.tsx
import React from &#39;react&#39;;
type IContextComposerProps = {
  contexts: React.ReactElement[];
};
const ContextComposer: React.FC<IContextComposerProps> = ({ contexts, children }) => {
  return (
    <>
      {contexts.reduceRight((child, parent) => {
        return React.cloneElement(parent,{},child);
      }, children)}
    </>
  );
};
export default ContextComposer;
// App.tsx
<ContextComposer
  contexts={[
      <ThemeContext.Provider value={"light"} />,
      <UserDataContext.Provider value={{ name: "ascodelife", age: 25 }} />,
      <LoadingContext.Provider value={{ loading, setLoading }} />,
  ]}>
    { children }
</ContextComposer>
Copy after login

After the transformation, when we pass parameters, it seems that we are really creating a component (of course, the component is actually created, but the component itself is not rendered to the virtual Dom, and is actually rendered. is a cloned copy). At the same time, the static type checking problem of value that we just focused on has also been solved.

tips: React.cloneElement(parent,{},child) is equivalent to React.cloneElement(parent,{children:child}), do you know why?

Related resources

The source code has been synchronized to github (https://github.com/ascodelife/react-context-provider-composer).

It has also been packaged into the npm warehouse (https://www.npmjs.com/package/@ascodelife/react-context-provider-composer). Welcome to experience it.

The above is the detailed content of Detailed steps for encapsulating React Context Composer (share). For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:juejin.im
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template