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
-
- It solves the problem of multi-layer transmission of component data and interaction of multi-layer components
-
- Redux provides multiple root data. Redux provides multiple root data. Redux provides multiple root data
-
- More specific scope, if redux loses too much data, it’s too heavy
-
- 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
— 完 —