By replacing ReDUx, I mean that not all cross-component data is redux. Themes, for example, are not suitable for redux

I think the advantages of Provider

    1. It solves the problem of multi-layer transmission of component data and interaction of multi-layer components
    1. Redux provides multiple root data. Redux provides multiple root data. Redux provides multiple root data
    1. More specific scope, if redux loses too much data, it’s too heavy
    1. Lighter weight, less complex operation than Redux

First send the article to the official document

React Context Official Chinese document

At the beginning of the text, if you don’t like the way to see the document, you can directly slide to the following [Basic Usage Example]

Basic method

React.createContext

Create a context whose value is used when the component renders a match from the Provider to the nearest context.

const MyContext = React.createContext(defaultValue);
Copy the code

MyContext has Provider and Consumer properties, and Provider is the component used outside (context rules).

For example, a page with a black theme and part of a white theme is structured as follows

<XXProvider value={black}>... <XXProvider value={white}>... < / XXProvider >... </XXProvider>Copy the code

Context.Provider

This is the component that initializes and uses in business, takes a value, and when the value changes, rerenders the data below.

Class.contextType

This is not used very often and is a rule breaker. Normally, the component complies with the Provider’s data, but it can be modified manually. The following is an official example.

class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* After the component is mounted, use the value of the MyContext component to perform some side effects */
  }
  componentDidUpdate() {
    let value = this.context;
    / *... * /
  }
  componentWillUnmount() {
    let value = this.context;
    / *... * /
  }
  render() {
    let value = this.context;
    /* Render based on the value of the MyContext component */
  }
}
MyClass.contextType = MyContext;
Copy the code

Context.Consumer

This is how to create a context, and this is how to use it. This is a component function that passes arguments to the following function, and the source should look like this

const Consumer = ({children}) = > {
    const values = 'I have a value'
    return children(values)
}
Copy the code

use

<MyContext.Consumer>
  {value= > /* Render based on context value */}
</MyContext.Consumer>
Copy the code

Context.displayName

React DevTools is designed to work with the React DevTools plugin

const MyContext = React.createContext(/* some value */); MyContext.displayName = 'MyDisplayName'; Provider < myContext. Provider> // "myDisplayName. Provider" in DevTools < myContext. Consumer> //" myDisplayName. Consumer" in In the DevToolsCopy the code

Basic Usage Examples

Let’s implement an example of reverse data transfer

Imagine we have a wallet page, and at the bottom of the page are some cards with a pop-up inside.

The component relationship of the page is

Wallet -> Card -> Dialog

purpose

We fetch data from the Wallet and pass it to the Dialog through the context

implementation

Create a provider file in the appropriate place, then create a context and export it

interface IAccountContext  {
  imageList: IImageListType[];
}
const accountContextDefaultValue = {
  imageList: [],}export const AccountContext = createContext<IAccountContext>(
  accountContextDefaultValue,
);
const { Consumer: AccountConsumer, Provider: AccountProvider } = AccountContext;
export { AccountProvider, AccountConsumer };
Copy the code

Once you’ve written it, you can use it anywhere you want, and we’ll use it on our Wallet page

import {AccountProvider} from './accountProvider.tsx'
const WalltePage = () = > {
    return <AccountProvider
      value={{
        imageList: [1],}} >
        <Card />
    </AccountProvider>
}
Copy the code

Once the provider is complete, you can easily use it anywhere below. There are two ways to use it: Consumer and Hooks.

const Dialog = () => {
    return <AccountConsumer>
    {({imageList}) => {
        return <div>dialog</div>
    }}
    </AccountConsumer>
}
Copy the code

This can be used in components, but it is not very convenient to use js, if you click, you can pass parameters, if you need to use JS from the beginning, this method is not very useful.

hooks

Dialog.tsx

const Dialog = () = > {
const { imageList } = useContext(AccountContext);
return <div>Dialog</div>
}
Copy the code

So this is the end of the process but what if we modify dynamic data modification? Let’s say we want a popover inside the card to have a button that can be clicked to open the card itself.

Advanced example

To implement this example, we need to add a hook function to the context value, which is provided by a component to call internally.

Here we add a function based on the above code, which is to add a button inside the popover. You can click on the button to open it. After opening the button, there will be the dynamic effect of drawing cards, but the dynamic effect itself exists beyond the popover. So the data is stored on the context.

To modify the context

The context file

import { IImageListType } from '@/service/type';
import { createContext } from 'react';

export type AccountContextUpdateOpenBoxTempInfo = (option: {
  id: string;
  img: string;
  open: boolean;
}) = > void;
interface openingBoxInfo {
  openBoxTempImage: string;
  isOpeningBox: boolean;
  openingBoxId: string;
  updateOpenBoxTempInfo: AccountContextUpdateOpenBoxTempInfo;
}
interface IAccountContext extends openingBoxInfo {
  imageList: IImageListType[];
  boxOpenAt: number;
  reloadFetch: () = > void;
}
const accountContextDefaultValue = {
  imageList: [].boxOpenAt: 0.reloadFetch: () = > {},
  openBoxTempImage: ' '.openingBoxId: ' '.isOpeningBox: false.updateOpenBoxTempInfo: () = >{}};export const AccountContext = createContext<IAccountContext>(
  accountContextDefaultValue,
);

const { Consumer: AccountConsumer, Provider: AccountProvider } = AccountContext;
export { AccountProvider, AccountConsumer };
Copy the code

After writing conNext, you only need to provide the specific logic in the provider

const Wallet: React.FC<IWalletProps> = ({}) = > {
  const [boxOpenAt, setBoxOpenAt] = useState(0);
  const reloadFetch = async () => {
    setCurrentPage((currentPage = defaultPageNum));
    fetchList();
  };
  const fetchList = async() = > {const overData = await fetchOverviewList({  ts, account: account ?? ' ', status, pageNum: currentPage, pageSize: 9}); setOverViewMockList( currentPage === defaultPageNum ? overData.list : [...overViewMockList, ...overData.list], ); setTotalPage(overData.total); };const [openBoxTempImage, useOpenBoxTempImage] = useState(' ');
  const [openingBoxId, setOpeningBoxId] = useState(' ');
  const [isOpeningBox, useIsOpeningBox] = useState(false);
  // Notice the line of code where the hook function is provided to the context and the component calls the hook function
  const updateOpenBoxTempInfo: AccountContextUpdateOpenBoxTempInfo = ({ img, open, id, }) = > {
    // Context data can be modified in real time, depending on hook data. If it is a class component, setData is used
    useOpenBoxTempImage(img);
    useIsOpeningBox(open);
    setOpeningBoxId(id);
  };
  return <AccountProvider
      value={{
        imageList.boxOpenAt.reloadFetch.openingBoxId.openBoxTempImage.isOpeningBox.updateOpenBoxTempInfo,}} >
      {/* CODE */}
      <Card />
  </AccountProvider>
}
Copy the code

Dialog file, in this case, we’re going to pull out the Dialog, because the Dialog itself is a pure popover, and inside the popover there are different states that generate different content, so content, let’s call it UnOpanBox. That’s where the content is triggered.

export const UnOpenBox: React.FC<{ onClose: () = > void; id: number} > =({ onClose, id, }) = > {
    const classes = useOpenBoxStyles();
    const { reloadFetch, updateOpenBoxTempInfo } = useContext(AccountContext);
    const onOpenBox  = async () => {
        updateOpenBoxTempInfo({ img: ' '.open: true.id: id.toString() });
        await new Promise((ok) = > setTimeout(ok, 5000));
        reloadFetch();
    }
    return <div className={classes.root}>
      <>
        <Button
          color="secondary"
          fullWidth
          variant="contained"
          onClick={onOpenBox}
        >
          Yes
        </Button>
        <div style={{ height: 15}} ></div>
        <Button
          color="secondary"
          fullWidth
          variant="outlined"
          onClick={onClose}
        >
          close
        </Button>
      </>
    </div>
}
const useOpenBoxStyles = makeStyles({
  root: {
    padding: '15px',}});Copy the code

At this point, the process is finished, the data has been modified, and the corresponding place can be changed. At this time, we monitor the head data in Card, and then modify the dynamic component.

Card.tsx

export const ProductRevealCard: React.FC<IRevealCard> = ({ id, url, status, title, }) = > {
  const { isOpeningBox, openingBoxId } = useContext(AccountContext);
  const [open, setOpen] = useState<boolean>(
    status === CardStatusEnum.COUNTDOWN,
  );
  const onClose = () = > {
    setOpen(false);
  };
  let Child = <></>;
  if (status === CardStatusEnum.UNOPENED) {
    Child = <RenderUnOpenBox id={id} onClose={onClose} />;
  }
  if (status === CardStatusEnum.COUNTDOWN) {
    Child = <RenderCountdownBox onClose={onClose} />;
  }
  const isOpenTemImg = Boolean(isOpeningBox && openingBoxId === id.toString());
  return (
    <div className={styles['card-box']} >
      <ImageCard
        onClick={()= > {
          setOpen(true);
        }}
        image={url}
        title={title}
      />
      <CardDialog
        open={
          isOpenTemImg
            ? false
            : status= = =CardStatusEnum.OPENED
            ? false
            : status= = =CardStatusEnum.PADDING
            ? true
            : open
        }
        title={getCardDialogTitle(status)}
      >
        {Child}
      </CardDialog>
      {isOpenTemImg && <OpenBoxTemView />}
    </div>
  );
};

const getCardDialogTitle = (status: CardStatusEnum) = > {
  return (
    <span>
      {status === CardStatusEnum.COUNTDOWN
        ? 'Reveal Countdown'
        : status === CardStatusEnum.PADDING
        ? 'NFT Is Minting'
        : status === CardStatusEnum.UNOPENED
        ? 'Reveal Now'
        : ''}
    </span>
  );
};
Copy the code

Ok, the whole process is complete, but since we are meticulous to this extent, we will paste the dynamic effect code.

OpenBoxTemView.tsx

import { CardMedia } from '@material-ui/core';
import _ from 'lodash';
import { useContext, useEffect, useState } from 'react';
import { AccountContext } from '.. /.. /provide/accountProvide';
import { useOpenBoxTempStyle } from './openBoxTemStyles';

let run = false;
export const OpenBoxTemView: React.FC<{}> = () = > {
  const temClasses = useOpenBoxTempStyle();
  const { imageList: list } = useContext(AccountContext);
  const [openBoxTempImage, setOpenBoxTempImage] = useState(' ');
  useEffect(() = > {
    run = true;
    const loop = () = > {
      if(! run)return;
      const item = list[_.random(0, list.length - 1)];
      if (item) {
        const url = item.image ?? item.url;
        setOpenBoxTempImage(url);
        setTimeout(() = > {
          loop();
        }, 100); }}; loop();return () = > {
      run = false; }; } []);return (
    <div className={temClasses.root}>
      {openBoxTempImage && (
        <CardMedia
          className={temClasses.temImg}
          component="img"
          image={openBoxTempImage}
        />
      )}
    </div>
  );
};
Copy the code

rendering

— 完 —