最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • react组件开发进阶实用技巧

    正文概述 掘金(渐行渐远君48901)   2020-12-30   588

    笔者在使用react框架近一年的过程中,总结了一些组件开发的实用技巧

    组件跨多层级数据共享

    如组件内需要共享一些数据,可以使用 react.createContext 配合 react.useContext 进行组件跨层级数据透传。 像Ant Design中,内部所有的组件实现都经过了一层包裹,用于共享 ConfigProvider 组件中的数据,其内部实现也是如此

    export const useGlobalProps = () => {
      return react.useContext(PropsCtx);
    };
    
    export const StoreCtx = createContext({});
    
    // 在共同的顶级组件中使用Provider注入数据
    const app = (props) => {
      const [name, setName] = react.useState("小明");
    
      const store = react.useMemo(
        () => ({
          name,
          setName,
        }),
        [name, setName]
      );
      return (
        <StoreCtx.Provider value={store}>
          <Child />
        </StoreCtx.Provider>
      );
    };
    
    const Child = (props) => {
      const { name = "" } = useGlobalProps();
      return <div>{name}</div>;
    };
    

    需要注意的是,provider 中如果数据会频繁变化,某些所关联的子组件即使没有用到该变化的数据,也会触发无必要的 render,此时要 用到react.useMemo,react.Memo,react.useCallback等函数进行手动优化

    使用 react.forwardRef 对 ref 进行透传

    在二次封装组件时,需要用到组件内的元素 ref 或内部子组件的 ref,可借助react.forwardRef进行透传

    import { Input } from 'antd'
    
    const CustomInput = React.forwardRef((props, ref) => {
      return (<div>
        <Input ref={ref}/>
      </div>)
    })
    
    // 使用
    const Test = (props) => {
      const ref = react.useRef()
      return <CustomInput ref={ref}>;
    }
    
    

    父组件调用子组件的内部方法

    在函数式组件开发中,在子组件中借助react.useImperativeHandle配合react.forwardRef,可向外层组件暴露出方法

    
    // ShareModal.tsx
    import { Modal, Button, Input, message } from 'antd'
    const ShareModal =  (props, ref) => {
      const [show, setShow] = react.useState(false)
    
      react.useImperativeHandle(ref, () => {
        return {
          open() {
            setShow(true)
          }
        }
      })
    
      return (
        <Modal
          maskClosable
          visible={show}
          footer={null}
          onCancel={()=>{
            setShow(false)
          }}
        >
          <div>
            body
          </div>
        </Modal>
      )
    }
    
    export default react.forwardRef(ShareModal)
    
    
    // 使用
    const Test = (props) => {
      const ref = react.useRef())
    
      const open= ()=>{
        ref.current.open()
      }
    
      return <div>
        <button onclick={open}>click me </button>
        <ShareModal ref={ref}>
      </div>
    
    }
    
    

    React.cloneElement 的使用

    使用场景通常是在需要统一处理子组件的一些逻辑,其实常用的方案有 HOC,props Render 等,使用React.cloneElement也可以做到 例如Ant Design中的Form表单组件,被Form.Item包裹住的表单组件,其 props 上都会被传入onChangevalue

    const Test = (props) => {
      const onChange = () => {};
    
      const renderChildren = () => {
        return React.Children.map(props.children, (child) => {
          if (child) {
            return React.cloneElement(child, {
              onChange,
              value: "123",
            });
          }
        });
      };
    
      return <div>{renderChildren()}</div>;
    };
    

    api调用式组件

    借助ReactDOM.render React.createElement ReactDOM.unmountComponentAtNode这三个 api,我们可以将普通组件封装为 api 式调用,一般使用场景像弹窗,toast 等组件,相比与使用 ref 调用组件中的方法,api 式使用起来更为方便

    //使用demo
    import Confirm from "./Confirm";
    
    const Test = (props) => {
      react.useEffect(() => {
        Confirm.show({
          title: "标题",
          content: "弹窗内容",
          onConfirm: () => {},
          onCancel: () => {},
        });
      }, []);
    
      return <div>test</div>;
    };
    

    实现

    //Confirm.tsx
    interface ConfirmModal{
      show:( opt: any)=> void
    }
    interface ConfirmModalProps{
    	title?: React.ReactNode | string
    	confirmText?: string
    	content?: React.ReactNode
    	head?: React.ReactNode
    	renderFoot?: (onConfirm: Function, onCancel: Function) => React.ReactNode
    	onCancel?: () => void
      onConfirm?: () => void
    }
    
    const Confirm = Object.create(null, {}) as ConfirmModal
    
    let singleContainer: HTMLDivElement | null = null
    
    const destroyComponent = (div: HTMLDivElement) => {
      if (!div) return
    	try {
    		let flag = ReactDOM.unmountComponentAtNode(div)
    		flag && document.body.removeChild(div)
      } catch(err) console.error(err)
    }
    
    //渲染弹窗
    Confirm.show = (opt: any) => {
      const { content ,onCancel ,onConfirm} = opt
      const createContainer = ()=>{
        let container = document.createElement('div')
        document.body.appendChild(container)
        if (!singleContainer) {
          singleContainer = container
        } else {
          singleContainer && destroyComponent(singleContainer)
        }
        singleContainer = container
      }
    
      createContainer()
    
    	const props:ConfirmModalProps = {
    		content: content,
    		confirmText,
    		title,
    		onCancel: function () {
    			onCancel && onCancel()
    			destroyComponent(singleContainer)
    		},
    		onConfirm: function () {
    				onConfirm && onConfirm()
    			destroyComponent(singleContainer)
    		},
      }
    
      ReactDOM.render(React.createElement(ConfirmModal, props), singleContainer)
    }
    
    
    // 弹窗组件的具体实现,只需要接受props进行处理即可
    import { Modal } from 'antd'
    const ConfirmModal = (props)=> {
      const { onConfirm , content,title, onCancel} = props
      const [visible, setVisible] = useState(true)
    
    	const onClose = () => {
    		setVisible(false)
    		onCancel && onCancel()
      }
    
      const onConfirm = ()=>{
        setVisible(false)
    		onConfirm && onConfirm()
      }
    
      return(
         <Modal
          maskClosable
          title={title}
          visible={visible}
          footer={null}
          onCancel={onClose}
          onOk={onConfirm}
        >
          <div>
              {props.content}
          </div>
        </Modal>
        )
    }
    
    

    起源地下载网 » react组件开发进阶实用技巧

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元