React |
您所在的位置:网站首页 › provid用法 › React |
上面说代替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 && ( )} ); };效果图 -- 完 -- |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |