likes
comments
collection
share

React:组件通信、事件机制

作者站长头像
站长
· 阅读数 41

一、组件、组件通信

组件,接收入参并返回用于描述页面的react元素。

1. class组件和函数(无状态)组件的区别

class组件和函数组件的转换如下:

  1. 定义一个同名的函数,参数是 props,
  2. 将 this.props 替换为 props,
  3. 去掉 render 方法。
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
function Welcome(props) {  
  return <h1>Hello, {props.name}</h1>
}

1.1 写法不同

1.2 函数组件捕获了渲染所使用的值:延迟输出

解释:react 中 props 是不可变的,但this一直是可变的。class组件每次调用this.props都会去获取最新的this值,因此无法保证当前读取到的props就是最初捕获到的。而函数组件props.user会在函数执行的瞬间就被捕获,也就是说函数组件中 「延迟输出」state 是调用时的state,而不是最新的state

class ProfilePage extends React.Component {
  showMessage = () => {
    alert('Followed ' + this.props.user);
  };
  handleClick = () => {
    setTimeout(this.showMessage, 3000);
  };
  render() {
    return <button onClick={this.handleClick}>Follow</button>;
  }
}

React的经典公式UI = f(data),也就是说UI是当前应用状态的一个函数,那么事件处理程序也是渲染结果的一部分,它应该是一个拥有特定propsstate的特定渲染。

然而对于类组件来说,调用一个回调函数读取一个 延迟的this.props 时,会打断这种关联,showMessage回调并没有与任何一个特定的渲染绑定在一起,所以它失去了正确的props。也就是说,从this中读取数据的这种行为,切断了这种联系。

  1. 怎么修复class组件的这个bug呢?可以提前读取this.props

  2. 如果想要读取最新的props和state,而不是捕获时的数据,可以采用:

    • class组件的this.props/this.state。
    • useRef。

1.3 性能优化

  • class组件:主要靠shouldComponentUpdate避免不必要的渲染。
  • 函数组件:通过React.memo组件缓存渲染效果来提升性能。
如果组件在相同的props情况下渲染相同的结果,就可以用`React.memo`保存该结果,
下次React可以跳过渲染组件的操作直接复用结果。
const MyComponent = React.memo(function MyComponent(props) {
  /* 使用 props 渲染 */
});

1.4 hooks之前的区别

  • class组件可以有生命周期、状态管理(state、setState)和this,而函数组件不具备。
  • 虽然class组件比较全面,但函数组件更灵活、容易测试、业务逻辑拆分也更方便。

2. 受控组件和非受控组件:针对表单元素

举个栗子~ :对于<input>元素,它自己维护了一个内部的state,能根据用户的输入自己进行UI更新(这个state不是我们看见的this.state)。

如果我们想要控制输入框的内容,可以在this.state上定义一个value属性等于input上的value属性,然后用onChange事件监听输入的变化并使用this.setState更新this.state.value

<input value={this.state.value} onChange={this.handleChange} />

1.受控组件:像html<input>等表单元素,我们通过将React 的 state和表单元素的值建立依赖关系,再通过onChange事件监听输入的变化并使用this.setState更新state。被React以这种方式控制取值的表单输入元素就叫受控组件

<input ref={eleRef} />

2.非受控组件数据不受外部的state控制,采用ref引用从dom中获取数据。

React推荐用受控组件来实现表单。」

3. 五种组件通信的方式

  1. 父组件 => 子组件:props
<Sub title = "早上好">
const Sub = (props) => { {props.title} }
  1. 子组件 => 父组件:回调函数,父组件将一个函数作为props传递给子组件,子组件调用该函数就可以向父组件通信。
<Sub callback = { this.callback.bind(this) } />
props.callback(msg)
  1. 隔代/跨级组件

    • 中间组件层层传递props
    • 使用context
  2. 兄弟组件:将数据来源存放在它们共同的父级组件中

  3. 不相关的组件:redux进行全局状态管理。

二、react17 事件机制

  • 原生事件:用addEventListener绑定的事件;
  • 合成事件:通过JSX绑定的事件,比如onClick={() => this.handleClick()}
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
    this.handleClick = this.handleClick.bind(this);  // 方法绑定
  }
  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
    e.preventDefault();  // 阻止默认行为
  }
  render() {
    return (
   // 1.小驼峰  2.传入的是一个函数作事件处理函数 3.不需要addEventListner,直接在初始渲染时添加监听器
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

合成事件 SyntheticEventReact模拟原生dom事件所有能力的一个事件对象,根据W3C规范来定义合成事件,能够兼容所有浏览器

const rootNode = document.getElementById('root'); 
ReactDOM.render(<App />, rootNode);
  1. React事件(handleClick)不是直接绑定到dom上面,而是绑定到渲染React树的根DOM容器中统一管理。
  2. 当事件发生并冒泡至根root时,React会使用统一的分发函数dispatchEvent执行回调。

原生事件和合成事件的区别

  • 事件命名方式不同:合成事件采用小驼峰式,原生事件是纯小写;
  • 使用JSX语法时需要传入一个函数作为事件处理函数,而不是一个字符串。
  • 阻止默认行为方式不同:原生事件可以返回falseReact需要preventDefault

参考文章

  1. react 核心开发者 dan :类组件和函数组件的区别
  2. 类组件和函数组件的区别
转载自:https://juejin.cn/post/7084915232149078052
评论
请登录