1. // react 元素 jsx对象
  2. const el = <div>jsx </div>
  3. // 函数组件
  4. const App = props => <div>app</div>
  5. // 类组件
  6. class RenderTextNode extends React.Component {
  7. render() {
  8. return <span>类组件--- class </span>
  9. }
  10. }

不同点

● 类组件需要继承class.Component - 函数组件不需要
● 类组件可以访问周期方法 - 函数组件不能
● 类组件中可以获取到实例化化后的this,基于this做各种操作 - 函数组件不能
● 类组件中可以定义并维护state - 函数组件不能
● 类组件可以结合生命周期,做一系列关于组件从挂载,到销毁的操作是一套很清晰的流程,- 函数组件不能
react是声明式的JavaScript库,可以将一些简短独立的代码片段组合成复杂的UI界面,定位就是拿到代码片段,输出页面UI。函数式组件的简介更符合声明式的JavaScript库这一理念

组件

组件是页面中UI视觉中的一部分, 可以复用的UI视图

可以使用简短、独立的代码片段组合成视图中的一部分。组件就是用来描述UI的一部分代码片段,拥有自己的状态

组件的拆分

减少代码量、抽离各个状态之间的关联关系
比如一个应用,很多个页面组成,相互跳转,而且没有加路由,可能一个文件就几百上千行,十几个state,不好维护。这个时候拆分组件就是为了优化逻辑,减少单文件的代码量

拆分成多个小组件,单个组件内部维护自己的状态,选择自己的props,组件内部就不需要关注其他组件的实现

根组件只负责数据的公用容器,各个组件之间的通信,保证数据的正常流通性

逻辑和组件的复用性
一个页面相同的视图,或者一同重复的逻辑拆份组件可能下一个组件中也会用到。不用再去从头写,拿过来直接用。

class类组件时候我们可以去使用HOC去实现逻辑或者UI的复用性。有了hooks,让我们的可以去复用逻辑,而不仅仅是复用组件UI,也更为方便。

如果不是专门的去写公共组件,业务组件很难作到复用性。而发现了一段相同类似的逻辑或者UI,可能会把当前组件往公共组件的方向靠拢

状态分离,减少更新量
react更新是从上到下的,如果一个组件内部更新,那么子组件也会更新。如果只有一个变更导致整个组件更新,这就过分了,状态之间不需要更新的也会参与。

拆分组件后,单个更新对没有关联的state不会产生影响,只会将状态发生变更的组件更新
比如:

import { useState } from "react";

function Child() {
  /*
    fetch ...
    compute... 8s
  */
  return <p>{num}</p>
}

function App() {
  const [ num, changeNum ] = useState(0);

  return (
    <div>
     <input value={num} onChange={evl => changeNum(evl.target.value)} />
      <p>num: {num} </p>
      <Child />
    </div>
  );

}

这种情况下Child组件会在input输入时都会参与更新,所以就可以拆分出来,比如:


function Input(props) {
  const {} = props;
  const [ num, changeNum ] = useState(0);

  return (<div>
       <input value={num} onChange={evl => changeNum(evl.target.value)} />
      <p>num: {num} </p>
      </div>);
}

这样input的更新就不会牵扯到Child的更新

再或者就是把Child组件提升到父级, 使用children的方式插入,也不会更新

容器组件和UI组件
容器组件是用来提供数据并且管理状态。然后再把状态传到UI组件的props当中去渲染. 而合理的拆分出来,使得容器组件只考虑组件数据关联的状态。视图组件只负责去渲染UI。

拆分出来,使得逻辑更加清晰,UI的变化真正取决去数据。UI组件不会看到各种逻辑,使得后续维护不需要关注的一部分

也是更难的,我们往往很难的去彻底拆离两种组件,二者都会相互掺杂一点

  • UI组件

ui代表用户页面,拿到数据渲染到页面
如果不是可复用的组件数据当然是props或者订阅本地状态管理器里的数据
ui组件不参与页面的逻辑只负责渲染当前的数据

  • 容器组件

拿到开发者写的页面中的逻辑代码,把数据整理好,放出去,给redux或者Children.用数据改变视图

容器组件不参与ui渲染,只关心数据的生成。

  • 受控组件

如果一个组件接收的props影响了组件内部的状态。就是受控组件

比如一个表单数据,在一个受控组件中,表单的数据是由react组件提供。推荐使用

而UI组件应该时纯粹的受控组件,展示的视图通过props来决定

  • 非受控组件

由组件内部维护的状态,不受props的影响就是非受控组件

如果拆分完组件,你应该知道当前组件为什么会被拆出来,拆出来又需要做什么事。此时组件的状态就不在需要考虑做受控还是非受控的纠结,(除公共组件外)

组合

React有十分强大的组合模式。我们推荐使用组合而非继承来实现组件间的代码重用 复制官网的一句话 组合还是mixins

组件可以接受任意 props,包括基本数据类型,React 元素以及函数来渲染到页面上

如果是非ui功能的,建议提取出来,封装成模块。导入使用就可以了。而不是使用继承的方式,而且继承的方式那么多,选一种能纠结死

一个页面,或者一个大型主组件可以有很多个子组件构建而成,或者说这本就是组合而成。而子组件也可以被其他页面去使用,虽然,继承更很专业,但组合更灵活。

HOC

高阶组件 Higher-Order Components

HOC高阶组件是一个共用逻辑达到可复用性的一种高级技巧,是一种react的组合特性而形成的设计模式。

HOC是纯函数,没有副作用。不会修改传入的参数。HOC不关心使用数据的方式,只关注自己要做什么功能。

高阶组件的参数是组件,返回值为一个新组件的函数。

hoc和mixin不同,mixin会改变原对象,而hoc不会侵入,只会在外部包裹一层,只做一个函数该做的事,提供一个或多个功能。像下面这样…
image.png

解决的问题

比如我们两个组件毫不相关的组件,只是为了展示页面中的同一种视图。而去向后端接口发送请求。这个请求,返回的数据还要取自己做处理,这个时候就可以使用高阶组件。

当然也可以把这个处理的函数单独抽出来,或者写成对于页面的方法。但,这些方法是属于单个接口的返回值而言,可以,但没必要。这个时候就可以使用高阶组件。还可以加点其他公共的样式进去,更为方便。

  1. 可以抽取重复代码。实现组件的复用,
  2. 条件渲染。控制组件的渲染逻辑。
  3. 捕获/劫持被处理组件的生命周期。

    使用

    将不相关的props传递给被包裹的组件,不要保留与当前操作不相关的props。不要做可能会影响包括的组件的操作

最大化组合的方式,不要去关联很多与当前函数操作不想关的数据,当前的操作只能是一个功能,不要去改变hoc的执行结果

不要在组件的render函数中使用HOC,第一会影响性能,第二,重新挂载组件的会导致改组件及其所有的子组件的状态丢失

不要保留组件的静态方法,比如ContetxtProps或者其他…。

refs不会被传递下去,如果你想可以通过其他的方式去解决


// 包裹组件
function wrapComponent(Component) {
  /*   ... 做点什么,比如一次网络请求 */
  return class Example extends React.Component {
    componentDidMount() {
      /* 做点什么... */
    }
    render() {
      return (
           <Component
              {...Object.assign({}, this.props, { data: "响应数据" })}
           />
      );
    }
  };
}

export default wrapComponent(function App({ data }) {
  return <div className="App">{data}</div>; // 响应数据
});

// 反向继承
const HOC = (WrappedComponent) =>
    class extends WrappedComponent {
      render() {
        return super.render();
    }
}
// 渲染劫持
const HOC = (WrappedComponent) =>
  class extends WrappedComponent {
    render() {
      if (this.props.isRender) {
        return super.render();
      } else {
        return null;
      }
    }
  }