ReactNeedAttention

发布时间 2023-09-19 11:26:54作者: 乐盘游

类组件各个部分的功能

// 方法的箭头函数写法(1)
class Index extends React.Component {
  constructor(...arg) {
    /* 执行 react 底层 Component 函数 */
    super(...arg);
  }
  state = {}; /* state */
  static number = 1; /* 内置静态属性 */
  handleClick = () =>
    console.log(111); /* 方法: 箭头函数方法直接绑定在this实例上 */
  /* 生命周期 */
  componentDidMount() {
    console.log(Index.number, Index.number1); // 打印 1 , 2
  }
  /* 渲染函数 */
  render() {
    return (
      // 这里原文是单引号,复制过来md文档的js代码块直接没识别出来
      <div onClick={this.handleClick()} style={{ marginTop: "10px" }}>
        hello,React!
      </div>
    );
  }
}

Index.number1 = 2; /* 外置静态属性 */
/* 方法: 绑定在 Index 原型链的 方法*/
Index.prototype.handleClick = () => {
  console.log(222);
};
// 类组件方法的另一种写法 组件方法(2)
class Index extends React.Component {
  constructor(props) {
    /* 执行 react 底层 Component 函数 */
    super(props);
    this.state = {
      originValue: 1,
    };
    // 注意这里的函数需要手动绑定在构造器上
    this.handleClick = this.handleClick.bind(this);
  }
  static number = 1; /* 内置静态属性 */
  handleClick() {
    console.log(111);
  }
  /* 生命周期 */
  componentDidMount() {
    console.log(Index.number, Index.number1); // 打印 1 , 2
  }
  /* 渲染函数 */
  render() {
    return (
      // 这里原文是单引号,复制过来md文档的js代码块直接没识别出来
      <div onClick={this.handleClick()} style={{ marginTop: "10px" }}>
        hello,React!
      </div>
    );
  }
}

问:上述绑定了两个 handleClick ,那么点击 div 之后会打印什么呢?

答:结果是 111 。因为在 class 类内部,箭头函数是直接绑定在实例对象上的,而第二个 handleClick 是绑定在 prototype 原型链上的,它们的优先级是:
实例对象上方法属性 > 原型链对象上方法属性

生命周期详解

https://juejin.cn/post/7046218136177082398?searchId=2023091817010725CC4F0E8FE9A5056D74

类组件事件中的 this 指向问题

  1. 箭头函数
    这种写法,每次 render 调用时都会重新创建一个新的事件处理函数。
class MyComp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      time: new Date(),
    };
  }
  handleClick() {
    console.log(this.state.time);
  }
  render() {
    return <button onClick={() => this.handleClick()}>按钮</button>;
  }
}
// this 指向当前组件的实例对象
  1. 组件方法
    render 调用时不会重新创建一个新的事件处理函数,但需要在构造函数中手动绑定 this。

还有一种选择是,我们可以在为元素事件属性赋值的同时绑定 this

class MyComp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      time: new Date(),
    };
    this.handleClick = this.handleClick.bind(this);
    // 通过 bind 将这个方法绑定到当前组件实例
  }
  handleClick() {
    console.log(this.state.time);
  }
  render() {
    return <button onClick={this.handleClick}>按钮</button>;
  }
}
  1. 属性初始化语法(ES7)
    使用官方脚手架 Create React App 创建的项目默认是支持这个特性的,可以在项目中引入 babel 的 transform-class-properties 插件获取这个特性支持。
class MyComp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      time: new Date(),
    };
  }
  handleClick = () => {
    console.log(this.state.time);
  }; // 也是箭头函数
  render() {
    return <button onClick={this.handleClick}>按钮</button>;
  }
}

函数组件

不要尝试给函数组件 prototype 绑定属性或方法,即使绑定了也没有任何作用,因为通过上面源码中 React 对函数组件的调用,是采用直接执行函数的方式,而不是通过 new 的方式。

函数组件和类组件本质的区别是什么呢?

对于类组件来说,底层只需要实例化一次,实例中保存了组件的 state 等状态。对于每一次更新只需要调用 render 方法以及对应的生命周期就可以了。但是在函数组件中,每一次更新都是一次新的函数执行,一次函数组件的更新,里面的变量会重新声明

为了能让函数组件可以保存一些状态,执行一些副作用钩子,React Hooks 应运而生,它可以帮助记录 React 中组件的状态,处理一些额外的副作用。

通信方式

React 一共有 5 种主流的通信方式:

  1. props 和 callback 方式
    这种常用父子间通讯
  2. ref 方式。
  3. React-redux 或 React-mobx 状态管理方式。
  4. context 上下文方式。
  5. event bus 事件总线。
    事件总线使用需要手动绑定与解绑,而且复杂工程不方便维护。由于事件总线使用的机会比较小,这里做个示例
import { BusService } from "./eventBus";
/* event Bus  */
function Son() {
  const [fatherSay, setFatherSay] = useState("");
  React.useEffect(() => {
    BusService.on("fatherSay", (value) => {
      /* 事件绑定 , 给父组件绑定事件 */
      setFatherSay(value);
    });
    return function () {
      BusService.off("fatherSay"); /* 解绑事件 */
    };
  }, []);

  const sonAction = (e) => {
    BusService.emit("childSay", e.target.value);
  };

  return (
    <div className="son">
      我是子组件
      <div> 父组件对我说:{fatherSay} </div>
      <input placeholder="我对父组件说" onChange={sonAction} />
    </div>
  );
}
/* 父组件 */
function Father() {
  const [childSay, setChildSay] = useState("");
  React.useEffect(() => {
    /* 事件绑定 , 给子组件绑定事件 */
    BusService.on("childSay", (value) => {
      setChildSay(value);
    });
    return function () {
      BusService.off("childSay"); /* 解绑事件 */
    };
  }, []);

  const fatherAction = (e) => BusService.emit("fatherSay", e.target.value);

  return (
    <div className="box father">
      我是父组件
      <div> 子组件对我说:{childSay} </div>
      <input placeholder="我对子组件说" onChange={fatherAction} />
      <Son />
    </div>
  );
}

类组件的继承

/* 人类 */
class Person extends React.Component {
  constructor(props) {
    super(props);
    console.log("hello , i am person");
  }
  componentDidMount() {
    console.log(1111);
  }
  eat() {
    /* 吃饭 */
  }
  sleep() {
    /* 睡觉 */
  }
  ddd() {
    console.log("打豆豆"); /* 打豆豆 */
  }
  render() {
    return <div>大家好,我是一个person</div>;
  }
}
/* 程序员 */
class Programmer extends Person {
  constructor(props) {
    super(props);
    console.log("hello , i am Programmer too");
  }
  componentDidMount() {
    console.log(this);
  }
  code() {
    /* 敲代码 */
  }
  render() {
    return (
      <div style={{ marginTop: "50px" }}>
        {super.render()} {/* 让 Person 中的 render 执行 */}
        我还是一个程序员! {/* 添加自己的内容 */}
      </div>
    );
  }
}
export default Programmer;

HOC 高阶组件

按照 hoc 主要功能,强化 props控制渲染赋能组件三个方向对 HOC 编写做了一个详细介绍,和应用场景的介绍,目的让大家在理解高阶组件的时候,更明白什么时候会用到?,怎么样去写?`
里面涵盖的知识点我总一个总结。

属性代理

强化 props & 抽离 state。
条件渲染,控制渲染,分片渲染,懒加载。
劫持事件和生命周期
ref 控制组件实例
添加事件监听器,日志

反向代理

劫持渲染,操纵渲染树
控制/替换生命周期,直接获取组件状态,绑定事件。

作者:我不是外星人
链接:https://juejin.cn/post/6940422320427106335

跨层级捕获 ref

/**
 *
 * @param {*} Component 原始组件
 * @param {*} isRef  是否开启ref模式
 */
function HOC(Component, isRef) {
  class Wrap extends React.Component {
    render() {
      const { forwardedRef, ...otherprops } = this.props;
      return <Component ref={forwardedRef} {...otherprops} />;
    }
  }
  if (isRef) {
    return React.forwardRef((props, ref) => (
      <Wrap forwardedRef={ref} {...props} />
    ));
  }
  return Wrap;
}

class Index extends React.Component {
  componentDidMount() {
    console.log(666);
  }
  render() {
    return <div>hello,world</div>;
  }
}

const HocIndex = HOC(Index, true);

export default () => {
  const node = useRef(null);
  useEffect(() => {
    /* 就可以跨层级,捕获到 Index 组件的实例了 */
    console.log(node.current.componentDidMount);
  }, []);
  return (
    <div>
      <HocIndex ref={node} />
    </div>
  );
};