React组件设计模式与最佳实践

React设计思想

U I = f ( d a t a ) X UI = f(data)X UI=f(data)X

  1. 在React中,界面完全由数据驱动
  2. 在React中,一切都是组件
    • 用户界面就是组件
    export default function Button {}
    
    • 组件可以嵌套包装,组成复杂功能
    • 组件可以用来实现副作用(ajax、修改dom、埋点等)
  3. props是组件间通讯的基本方式,还有context,redux,event bus等

定义清晰可维护的接口

设计原则

  • 保持接口小,props属性少

  • 合理拆分组件,根据数据边界划分组件,充分组合

  • 把state往上层组件提取,让下层组件只需要实现为纯函数

  • 保持小的更新,细粒度且合理拆分组件,react的diff比对是以组件为单位进行的

最佳实践
  • 避免render函数,访问同样的props和state,代码依然耦合在一起
  • 命名:给回调类型的函数加统一前缀 on开头 (onStart, onSubmit)
  • 对参数定义类型约束
import PropTypes from 'prop-types'
const ControlButton=(props)=>{
    //todo
}
ControlButton.propTypes={
    height: PropTypes.number,
    content: PropTypes.string.isRequired 
}

组件内部实现

功能正常、代码整洁、高性能

1. 组件代码拆分

每个组件都有自己专属的代码文件

const BaseInfo = (props) => {
};

export default BaseInfo;

代码整洁,易维护,组件互不影响

2. 用解构赋值获取props的属性
const BaseInfo = ({activated, onStart, onPause, onReset, onSplit}) => {

}

简化代码,参数一目了然,默认值

3. class组件,利用属性初始化定义state和成员函数
class SellInfo extends React.Component {
  //this.state, this.onClick=this.onClick.bind(this)
  state = {
    isStarted: false,
  }
  onClick = () => {
    this.setState({
      isStarted: true,
    });
  }
}

好看且性能好,保留this

组件设计模式

  • 傻瓜组件和聪明组件
  • hoc模式
  • render props模式
  • 提供者模式
  • 组合模式

1. 聪明组件和傻瓜组件

  • 别名:

    • 有状态组件和无状态组件
    • 容器组件和渲染组件
  • 优点

    • 责任分离, 聪明组件负责管理数据状态,傻瓜组件负责展示数据
    • 如果要优化界面,只需要去修改傻瓜组件,如果想改进数据管理和获取,修改聪明组件
2. 聪明组件和傻瓜组件:提升性能
  • class: PureComponent,实现shouldComponentUpdate浅比较

  • hoc:React.memo

  • hooks: useMemo, useCallback

2.1 React.memo
  • 默认情况浅层比较,传入第二个参数,可自定义深层比较
function MyComponent(props) {
  /* render using props */
}
function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
}
export default React.memo(MyComponent, areEqual);
2.2 useMemo
  • 第二个参数为依赖项,执行回调函数
export default (props = {}) => {
    console.log(`--- component re-render ---`);
    return useMemo(() => {
        console.log(`--- useMemo re-render ---`);
        return <div>
            {/* <p>step is : {props.step}</p> */}
            {/* <p>count is : {props.count}</p> */}
            <p>number is : {props.number}</p>
        </div>
    }, [props.number]);
}
2.3 useCallback
  • useCallback 缓存钩子函数,useMemo 缓存返回值
const isEvenNumber = useMemo(() => {
    return count % 2 === 0;
  }, [count]);

  const onClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);
   return (
    <div>
      <div>{count} is {isEvenNumber ? 'even':'odd'} number</div>
      <button onClick={onClick}></button>
    </div>
  );

3. hoc模式

const OrderList = () => {
  if (getUserId()) {
    return ...; // 显示订单列表
  } else {
    return null;
  }
};
const ProductList = () => {
  if (getUserId()) {
    return ...; // 显示商品列表
  } else {
    return null;
  }
};
3.1 hoc模式:抽取共同逻辑,强化组件
const withAuth = (Component) => {
  //强化操作
  const NewComponent = (props) => {
    if (getUserId()) {
      return <Component {...props} />;
    } else {
      return null;
    }
  }
  return NewComponent;
};
const OrderList = withAuth((props) => {
  return ...; // 显示订单列表
});
3.1 hoc特点
  • 高阶组件是一个纯函数,传入组件,返回一个新的组件,包含参数组件。
  • 高阶组件不能去修改作为参数的组件,一般把传给自己的 props 转手传递给作为参数的组件
  • 命名一般以with开头
  • 使用场景
    • 权限控制,统一对页面进行权限判断,按不同条件渲染不同内容
    • 代码复用,可以将重复的逻辑进行抽象
3.2 hoc优缺点
  • 优点
    • 抽离和复用逻辑
    • 条件渲染,控制渲染等
  • 缺点
    • 组件多层嵌套, 增加复杂度与理解成本,相同命名的props会覆盖老属性,不清楚props来源
    //假如this.props中含有name 
    const newProps = {name: "cqm",value: "testData"}
    return <ComponentClass {...this.props} {...newProps} />
    

4. render props 模式

  • props作为函数的参数来渲染部分页面
const RenderAll = (props) => {
  return(
     <React.Fragment>
      {props.children(props)}
     </React.Fragment>
  );
};
<RenderAll>
    () => <h1>hello world</h1>}
</RenderAll>
4.1 传递props
const Login = (props) => {
  const userName = getUserInfo();

  if (userName) {
    const allProps = {userName, ...props};
    return {props.children(allProps)};
  } else {
    return null;
  }
};

<Login>
  {({userInfo}) => <h1>Hello {userName}</h1>}
</Login>
4.2 不局限于children
const Auth= (props) => {
  const userName = getUserName();

  if (userName) {
    const allProps = {userName, ...props};
    return {props.login(allProps)};
  } else {
    retrun {props.nologin(props)}
  }
};
<Auth
    login={({userName}) => <h1>Hello {userName}</h1>}
    nologin={() => <h1>Please login</h1>}
/Auth>
4.3 依赖注入
  • 逻辑A依赖于逻辑B和C(A->B、A->C )。依赖注入就是把 B 的逻辑以函数形式传递给 A,A 和 B 达成函数接口一直,逻辑C也可以重用逻辑 A。
  • Login 和 Auth相当于逻辑 A,而传递给组件的函数类型 props,就是逻辑 B 和 C
4.4 和高阶组件的比较
  • props命名可修改,不存在相互覆盖
  • 清楚props来源
  • 比高阶组件更加灵活和简单
  • 不会出现组件多层嵌套,但存在函数回调形式的多层嵌套

5. 提供者模式

场景:假如组件A和组件X之间隔着多层组件,需要传递数据,一层一层传递太麻烦,对于这种跨组件的数据传递,需要提供者模式

5.1 Provider
  • Provider 用于提供 context
//创建context
const ThemeContext = React.createContext();
//提供者
function ThemeProvider(){
    const theme = { color:'pink' }
    return <ThemeContext.Provider value={ theme } >
        <Index />
    </ThemeContext.Provider>
}

5.2 Consumer
  • Consumer 用于消费 context ,render children 函数中第一个参数就是保存的 context
function ThemeConsumer(props){
    return <ThemeContext.Consumer>
      { (theme)=>{ /* render children函数 */
          const { color } = theme
          return <p style={{color }}>
           {props.children}
       </p>
      } }
    </ThemeContext.Consumer>
}
5.2 提供者模式原理
  • 通过 Consumer 订阅 context 变化,context 改变,订阅者状态更新
  • 使用场景:全局传递状态、保存状态、外层组件项内层组件传值。

6. 组合模式

  • 组合模式适合一些容器组件场景,通过外层组件包裹内层组件

  • 这种方式在 Vue 中称为 slot 插槽,外层组件可以轻松的获取内层组件的 props 状态,还可以控制内层组件的渲染,

  • 组合模式能够直观反映出 父 -> 子组件的包含关系

6.1 组合模式实例
  <Tabs onChange={ (type)=> console.log(type)  } >
      <TabItem name="react"  label="react" >React</TabItem>
      <TabItem name="vue" label="vue" >Vue</TabItem>
      <TabItem name="angular" label="angular"  >Angular</TabItem>
  </Tabs>
  • Tabs 负责展示和控制对应的 TabItem 。绑定切换 tab 回调方法 onChange。当 tab 切换的时候,执行回调。

  • TabItem 负责展示对应的 tab 项,向 Tabs 传递 props 相关信息。

6.2 组合模式的原理
  • 在容器组件中通过props.children获取子组件的children,做进一步处理

  • 可以对子组件做类型判断、容器的props混入子组件、子组件传入props的判断控制渲染

  • 既然可以混入容器组件的props,进而可以传递方法参数组件通信

点击加入群聊【小程序/前端交流】,一起学习交流:663077768

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kiki·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值