Refs 转发
接上文,开始钻研refs转发的起因是因为,antd4更新的form表单更改了3的数据获取和办法调用的形式,须要在应用前应用相似于 this.refForm = React.createRef()
来定义,获取的时候通过 this.refForm.current
的形式来应用form自带的函数和数据,于是对refs转发产生了趣味。
在一开始接触ref的时候是在vue外面,再vue外面,通过ref来操作子组件的函数,通过emit来调用父组件函数:
<template> <div ref='child' value="test"></template>methods:{ testRef:function () { console.log(this.$refs.child) //获取上文的div实例 }}
而在react中,根本实现相似,不过就是将结构refs参考实例进行了前置,须要先结构能力用,比方想要管制一个组件外部的某些实在dom,须要留神的是,这时候的ref并不是通过props传递上来到div的dom中,而是获取了RefButton组件的实例,具体实现如下:
/** @desc 先定义一个组件式的button,在外部咱们定义了一个简略的click事件,待会再外层调用它*/import React, { Component } from "react";import styles from "./styles.module.less";class RefButton extends Component { constructor(props) { super(props); this.state = { test: "测试用数据"}; } onClick = () => { const { test } = this.state; this.setState({ test: test + 1 }); }; render() { const { test } = this.state; return ( <div className={styles.contain}> {test} ........ </div> ); }}export default RefButton;/** @desc 父组件*/class Refs extends Component { constructor(props) { super(props); this.state = { test: "新改的数据" }; this.ref = React.createRef(); //定义一个ref } onClick = () => { const { test } = this.state; const message = this.ref.current; //调用之前定义的ref message.setState({ test: test + 1, }); this.setState({ test: test + 1, }); }; render() { return ( <div className={styles.contain}> <RefButton ref={this.ref} /> //让定义的ref传递上来 <button className={styles.test_button} onClick={() => this.onClick()}> 测试ref按钮 </button> <Parent /> </div> ); }}export default Refs;
此外,还有比拟新的hook的用法,我只写了一个简略的例子,想要看细节能够去看看《对于useRef的用法》,外面具体介绍了该hook的根本应用,demo如下:
/** @desc 定义了两个函数组件,parent和child*/import React, { useImperativeHandle, useRef, useState } from "react";// 引入useState 、useRef 、 useImperativeHandle组件// 子组件function Child(props, ref) { const [number, setNumber] = useState(0); const funcref = useRef(); // 通过应用useImperativeHandle来给父组件传递子组件办法,为受控组件的更新和保护进行数据传递 useImperativeHandle(ref, () => ({ // 定义了一个递增函数,父组件单击按钮触发子组件的addNumber函数,给input中的数加一 addNumber(value = 1) { typeof value === "number" && setNumber(value + number); }, getFocus() { funcref.current.focus(); }, })); return ( <> <input type="text" ref={funcref} value={number}></input> </> );}// 用forwardRef包裹子组件,使得父组件的ref穿透传递上来// 我这里只包了一层,实践上来说无论多少层,只有在外部包裹了// 都是能够获取到ref的const ForwardChild = React.forwardRef(Child); // 父组件function Parent() { // let [number, setNumber] = useState(0); // 在应用类组件的时候,创立 ref 返回一个对象,该对象的 current 属性值为空 // 只有当它被赋给某个元素的 ref 属性时,才会有值 // 所以父组件(类组件)创立一个 ref 对象,而后传递给子组件(类组件),子组件外部有元素应用了 // 那么父组件就能够操作子组件中的某个元素 // 然而函数组件无奈接管 ref 属性 <Child ref={xxx} /> 这样是不行的 // 所以就须要用到 forwardRef 进行转发 const inputRef = useRef(); //{current:''} function getFocus() { inputRef.current.getFocus(); } function addMenber() { inputRef.current.addNumber(); } return ( <> <div className={styles.test_parent}> 测试函数组件 <ForwardChild ref={inputRef} /> <button onClick={addMenber}>+</button> <button onClick={getFocus}>获取焦点</button> </div> </> );}
官网文档也说了,针对HOC(高阶组件),refs转发很有用,就比方我一开始说的antd 的form表单,然而他并不是把ref作为props传递上来了,他只是获取了高阶组件返回的组件的实例,如果想要在组件外部应用父组件ref的,则能够通过 React.forwardRef()
来将ref传递上来,该办法能够透传。
PS:须要留神如果想要让ref当作props传递上来,不能再应用ref作为变量名
Fragments
我的了解就是通明层,看起来有货色,其实都是骗零碎的,让他认为这里有最外层包裹了,不会给你找麻烦,毕竟很多时候想要封装组件,必须有个最外层,而有的组件其实是并列的关系,多包一层,即便是空的 div
也是会出问题的,比如说罕用的例子:
<table> <tr> <div> <td></td> <td></td> </div> </tr></table>
看起来两头那层div是没什么货色的,然而它就会让你的table无奈渲染进去。这时候应用 <Fragment>{Child}</Fragment>
或 <React.Fragment>{Child}</React.Fragment>
对子组件进行包裹就能够防止这样的问题,此外React还提供该语法的简写 <>{Child}</>
这样的空标签也能够达到雷同的目标。
高阶组件
高阶组件就是一种比拟特地的函数,该函数以组件为参数,返回后果也是一个组件。次要是针对一些复用性较高,然而又须要有肯定的自定义操作,比方整个组件的业务逻辑统一,区别在于传入的对象不统一或者是其中一个函数不统一,如果是一般的写法,要么在组件内加很多的判断,当这种判断不是一种两种,而是极多,带有不确定的状况的时候,就须要有一个相似于中间件的货色来解决现有组件,以实现相应的逻辑,于是就有了高阶组件,集体认为是 组件的直达函数
。如下是一个简略的HOC demo,具体的流程见正文和代码:
/** @desc 结构了一个简略的hoc函数,传入组件,而后承受一个函数,扭转组件的内容*/function hocFunction(Comp, func) { class hocFunction extends React.Component { constructor(props) { super(props); this.state = { data: func(), }; } componentDidUpdate(prevProps) { console.log("Current props: ", this.props); console.log("Previous props: ", prevProps); } render() { // 过滤掉非此 HOC 额定的 props,且不要进行透传 const { extraProp, ...passThroughProps } = this.props; // 将 props 注入到被包装的组件中。 // 通常为 state 的值或者实例办法。 const injectedProp = this.state.data; return <Comp injectedProp={injectedProp} {...passThroughProps} />; } }; // 这是react进行的约定,因为容器组件也会显示在调试中,所以尽量都要给他命名 hocFunction.displayName = `hocFunction(${getDisplayName(hocFunction)})`; return hocFunction;}function HocTest(props) { return ( <> <p> {props.injectedProp}:test all the hoc will be change. </p> </> );}class Hoc extends Component { constructor(props) { super(props); this.state = { test: "", }; this.LogTestHoc = hocFunction(HocTest, () => { return 20; }); } changeProps = () => { const { test } = this.state; this.setState( { test: test !== "" ? "" : "这是一个测试用的数据", }, () => { this.LogTestHoc = hocFunction(HocTest, () => { return test; }); } ); }; render() { const { test } = this.state; const LOGHOC = this.LogTestHoc; return ( <div className={styles.contain}> <ErrorBoundary> <LOGHOC test={test}></LOGHOC> <button id="buttonC" onClick={this.changeProps}> 测试更新props </button> </ErrorBoundary> </div> ); }}export default Hoc;
该demo在constructor中定义hoc,并且在单击更新按钮时触发hoc的扭转,从而使得hoc包裹的HocTest组件发生变化。
PS: 不能在render种定义hoc,这会造成每次调用 render 函数都会创立一个新的hoc,这将导致子树每次渲染都会进行卸载,和从新挂载的操作!
与第三方库的协同
没怎么看
深刻JSX
官网说的很分明:
实际上,JSX 仅仅只是 React.createElement(component, props, ...children)
函数的语法糖
次要内容就是将一些JSX中的一些禁忌和应用办法
1、不论你用没用,只有写了就肯定要import引入到以后作用域
2、能够应用点语法来调用react自带的办法组件等,如 React.Fragment
3、如果你想应用一个组件,组件名必须大写,定义的时候没大写,应用的时候也要通过赋值给大写的变量
4、不能在应用组件的时候进行判断,变量等,必须在应用前就扭转。如:
const components = { photo: PhotoStory, video: VideoStory};function Story(props) { // 谬误!JSX 类型不能是一个表达式。 return <components[props.storyType] story={props.story} />;}function Story(props) { const Comp = components[props.storyType] // 正确! return <Comp story={props.story} />;}
5、能够在props中应用表达式:value={1+2+2+3}
6、字符串字面量能够间接作为props传递
7、props的默认值时true,比方:autoplay
和 autoplay={true}
时等价的
8、能够通过开展运算符写入props ,比方:<Demo {...props} />
9、在JSX中字符串、子组件、函数都能成为子元素
10、能够有多个子元素:
<Comp> <First /> <Second /></Comp>
11、能够通过 this.props.children
来获取子元素,这在定义外层容器中非常有用;
//定义一个Comp的函数组件,在子组件外层包裹一个divfunction Comp(props){ const {children , ...other} = props return <div {...other}>{Children}</div>}
性能优化
没有怎么看
Portals
第一眼看到这个的时候,有一点惊喜的,作为一个萌新前端,之前在本人写组件的时候,就碰到过想要把组件的一部分渲染在组件的外层(已经写了一个相似于select的组件,开展后被内容遮挡,设置z-index又会让未开展的时候显示异样)。
该办法的外围就是 React.createPortal(this.props.children, this.el)
办法,该办法是承受一个子元素,将其挂载到一个元素上,上面是一个简略的demo,创立一个 div
,将 div
挂载到 body
内,而后把子元素置于 div
中。感兴趣的能够尝试本人写一个简略的modal,上面就是一个madal 的一部分:
class Demo extends Component { constructor(props) { super(props); this.state = { visible: false }; // 先创立一个div this.el = document.createElement("div"); } componentDidMount() { // 在组件生成时断定状态,如果是visible = true就调用生成子组件的函数 if (this.props.visible !== this.state.visible) { this.initDom(); this.setState({ visible: this.props.visible }); } } componentDidUpdate(preProps, nextProps) { // 在组件更新时再进行断定 if (this.props.visible !== nextProps.visible) { this.initDom(); nextProps.visible = this.props.visible; } } componentWillUnmount() { //在组件卸载时捣毁之前创立的div document.body.removeChild(this.el); } initDom = () => { const { destory, visible } = this.props; let self = this; // 将之前创立的div置入body document.body.appendChild(this.el); // 定义了一个蒙版的底层 this.el.setAttribute("class", "select_modal_box"); this.el.style.cssText = `position: absolute;width: 100%;height: 100%;top:0;background:rgba(0,0,0,0.3)`; // 定义了一个函数监听蒙版的单击事件,接管外界的destory和visible this.el.addEventListener( "click", function (event) { if (event.target.className === "select_modal_box") { console.log("456456465", event.target); if (destory) { self.props.onCancel(); let doc = document.getElementById("select_modal_box"); console.log(event.currentTarget); event.currentTarget && doc && document.body.removeChild(event.currentTarget); } else { self.props.onCancel(); this.style.visibility = "hidden"; } self.setState({ visible: false, }); } }, false ); // 如果visible= false 暗藏modal !visible ? (this.el.style.visibility = "hidden") : console.log("test"); }; render() { // 返回一个挂载到div上的子组件,子组件由内部传入。 return ReactDOM.createPortal(this.props.children, this.el); }}
Profiler
未完待续。。。