React

您所在的位置:网站首页 provid用法 React

React

#React| 来源: 网络整理| 查看: 265

上面说代替redux指的是,不是所有跨组件的数据都用redux。比如主题就不适合用redux了

我自认为Provider的优点

解决了组件数据多层传递问题、多层组件交互问题 多个根数据,这个直接和redux区分开了, 很适合做主题. provider也是React自带,不需要安装 范围更明确,如果redux数据丢太多了,太重了 更轻量,没有redux多么多复杂的操作

文章先送下官方文档哈

React Context官方中文文档

正文开始,如不喜欢看文档的方式,可直接滑动到下面【基本使用例子】

基本方法 React.createContext

创建一个上下文, 当组件渲染匹配从Provider匹配到了最近的上下文,就会使用它的值。

const MyContext = React.createContext(defaultValue);

MyContext里面有Provider、Consumer属性,Provider就是在外层使用的组件(遵守上下文规则)

比如,一个页面黑色主题,有一部分是白色主题,这时候结构如下

。。。 ... 。。。 Context.Provider

这个就是初始化和在业务使用的组件,接受一个value的值,当value的值发生改变的时候,会重新渲染下面的数据。

Class.contextType

这个不常使用,这个是破坏规则的,正常来说组件遵守的是Provider的数据,但是如果手动修改制定也是可以的。 如下官方示例。

class MyClass extends React.Component { componentDidMount() { let value = this.context; /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */ } componentDidUpdate() { let value = this.context; /* ... */ } componentWillUnmount() { let value = this.context; /* ... */ } render() { let value = this.context; /* 基于 MyContext 组件的值进行渲染 */ } } MyClass.contextType = MyContext; Context.Consumer

上面指的是如何创建一个context,这个就是如何使用它。这个是一个组件函数,会给下面的函数传递参数,源码应该如下

const Consumer = ({children}) => { const values = '我有一个值' return children(values) }

使用方式

{value => /* 基于 context 值进行渲染*/} Context.displayName

主要是为了配合浏览器插件(React DevTools),给开发使用的

const MyContext = React.createContext(/* some value */); MyContext.displayName = 'MyDisplayName'; // "MyDisplayName.Provider" 在 DevTools 中 // "MyDisplayName.Consumer" 在 DevTools 中 基本使用例子

我们实现一个数据逆传递的例子

设想一下我们有一个钱包页面,页面下面有一些卡片,卡片里面有一个弹窗。

这时候页面的组件关系为

Wallet -> Card -> Dialog

目的

我们在Wallet里面获取数据,然后把数据通过context传递给Dialog

实现

先在合适的位置创建一个provider文件,然后创建一个context并导出

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

编写完成后就可以找地方随便使用啦,这时候我们在Wallet页面使用它

import {AccountProvider} from './accountProvider.tsx' const WalltePage = () => { return }

这时候provider完成后,下面的任何地方都可以很方便使用。使用方式有两种,一种是Consumer, 一种是Hooks 函数.

const Dialog = () => { return {({imageList}) => { return dialog }} }

这样就可以在组件里面使用啦,但是不太方便js使用,如果是点击的话,可以传参数,如果是js一开始就需要用,这个方式就不太好用了。

hooks

Dialog.tsx

const Dialog = () => { const { imageList } = useContext(AccountContext); return Dialog }

到这里 走完流程了 但是如果我们修改动态数据修改怎么办?比如说我们想要卡片内部的弹窗有一个按钮,点击后可以卡片本身。

高级例子

实现这个例子,我们需要在context值加一个钩子函数,提供给某一个组件内部调用, 这里基于上面的代码添加吧。

这里我们基于上面代码添加一个功能,就是在弹窗内部加一个按钮,按钮上面可以点击开启,开启后会有抽卡片的动效,但是动效本身是超越弹窗而存在的。也就是数据是寄存在context上面。

先修改context

context文件

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( accountContextDefaultValue, ); const { Consumer: AccountConsumer, Provider: AccountProvider } = AccountContext; export { AccountProvider, AccountConsumer };

写好connext后只需要在provider提供具体逻辑即可

const Wallet: React.FC = ({}) => { 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); // 注意这行代码,钩子函数提供给context,最后组件调用钩子函数 const updateOpenBoxTempInfo: AccountContextUpdateOpenBoxTempInfo = ({ img, open, id, }) => { // context数据可能实时修改,是依靠hook数据,如果是class组件,就setData,官方就是class的例子 useOpenBoxTempImage(img); useIsOpeningBox(open); setOpeningBoxId(id); }; return {/* CODE */} }

Dialog 文件, 在这里,我们把Dialog抽出来,因为Dialog本身是纯粹的弹窗,弹窗内部会有不同状态生成不同的内容,所以内容,我们就叫他UnOpanBox吧。,也就是在这里去触发内容。

export const UnOpenBox: React.FC 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 Yes close } const useOpenBoxStyles = makeStyles({ root: { padding: '15px', }, });

到这里流程就走完了,数据已经修改了,对应的地方改变即可,这时候我们在Card里面去监头数据,再修改动效组件。

Card.tsx

export const ProductRevealCard: React.FC = ({ id, url, status, title, }) => { const { isOpeningBox, openingBoxId } = useContext(AccountContext); const [open, setOpen] = useState( status === CardStatusEnum.COUNTDOWN, ); const onClose = () => { setOpen(false); }; let Child = ; if (status === CardStatusEnum.UNOPENED) { Child = ; } if (status === CardStatusEnum.COUNTDOWN) { Child = ; } const isOpenTemImg = Boolean(isOpeningBox && openingBoxId === id.toString()); return ( { setOpen(true); }} image={url} title={title} /> {Child} {isOpenTemImg && } ); }; const getCardDialogTitle = (status: CardStatusEnum) => { return ( {status === CardStatusEnum.COUNTDOWN ? 'Reveal Countdown' : status === CardStatusEnum.PADDING ? 'NFT Is Minting' : status === CardStatusEnum.UNOPENED ? 'Reveal Now' : ''} ); };

ok,整个流程完成,不过既然都细致到这个程度了,再把动效果代码贴上吧。

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 ( {openBoxTempImage && ( )} ); };

效果图

61440d43196d4_61440d4333831.gif

-- 完 --



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3