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,比方:autoplayautoplay={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

未完待续。。。