• react 读书笔记


    来源掘金小册 - react实战:设计模式和最佳实践

    react设计思想

    1. 属性展开 - 保留当前组件需要的props,并且使其他的props传递下去
      var obj = {a: 1, b: 2, c: 3, d: 4, e: 5}
      const { a, ...other } = obj
      console.log(other)  // {b: 2, c: 3, d: 4, e: 5}
    
    1. 在react中,界面完全由数据驱动
    2. 在react中,一切都是组件
    3. props是react组件之间通讯的基本方式
      // 可以实现记录用户访问路径操作信息,每次进入某个页面或者功能的时候都会访问一个线上资源
      // a.js
      export default Beacon extends React.Component {
        componentDidMount() {
          const beacon = new Image()
          beacon.src = 'https://sxc.image/i.png'
        }
        
        render() {
          return null
        }
      }
      // b.js
      render() {
        <div>
          <Beacon/>
        </div>
      }
    
    
      // 代码组织方式优化 - renderXXX函数访问的是同样的props和state,代码耦合严重
      class StopWatch extends React.Component {
    
        renderClock() {
    
        }
    
        renderButtons() {
    
        }
    
        renderSplitTimes() {
    
        }
        
        render() {
          const Clock = this.renderClock()
          const Buttons = this.renderButtons()
          const SplitTimes = this.renderSplitTimes()
    
          return (
            <div>
              {Clock}
              {Buttons}
              {SplitTimes}
            </div>
          )
        }
      }
      // 更好的组织方式
      class StopWatch extends React.Component {
        render() {
          return (
            <div>
              <Clock />
              <Buttons />
              <SplitTimes />
            </div>
          )
        }
      }
      const Clock = (props) = {
    
      }
      const Buttons = (props) => {
    
      }
      const SplitTimes = (props) => {
    
      }
    

    设计react组件原则及最佳实践

    1. 保持接口小,props数量要少
    2. 根据数据边界来划分组件,充分利用组合
    3. 把state往上层组件提取,让下层组件只需要实现为纯函数
    4. 避免renderXXXX函数
    5. 给回调函数类型的props加统一前缀,比如on or handle - onStart
    6. 使用propTypes来定义组件的props
    7. 尽量每个组件都有自己专属的文件
    8. 用解构赋值的方式获取参数props的属性 - const { a, b } = { a: 1, b: 2 }
    9. 利用属性初始化来定义state和成员函数 - 给默认值 const { data = [] } = this.props
    
      /*
        1. 尽量不要在jsx中写内联函数
        2. 每次渲染,都会产生新的函数对象
        3. 每次传给组件的props(方法,数据,状态等)都是新的,无法通过shouldComponentUpdate对props的检查来避免重复渲染
      */
      <Buttons
        activated={this.state.isStarted}
        onStart={() => {/* TODO */}}
        onReset={() => {/* TODO */}}
      />
    
      /*
        1. style属性的使用
        2. 内联样式在组件每次渲染时都会创建一个新的style对象
        3. 如果样式不发生改变,应该把style对象提取到组件之外,可以重用一个对象
      */
      // bad
      const Clock = (props) => {
        return <h1 style={color: '#ccc'}>小姐姐真好看<h1/>
      }
      // good
      const style = {
        color: '#ccc'
      }
      const Clock = (props) => {
        return <h1 style={style}>小姐姐真好看<h1/>
      }
    

    组件样式

    1. 不同组件中相同的元素样式相互影响,a.js与b.js中都有h1组件,样式之间会相互影响
    2. 添加styled-jsx/styled-component支持
    
      // a.js
      index.css
      h1: {
        color: red
      }
      <h1>a</h1>
      // b.js
      <h1>b</h1>
      
      // a.js
      .a h1: {
        color: red
      }
      <div className='a'>
        <h1>a</h1>
      <div/>
      // b.js
      <div className='b'>
        <h1>a</h1>
      <div/>
    

    状态组件与无状态组件分割

    1. 软件设计原则"责任分离",就是让一个模块责任尽量少,每个模块专注于一个功能,有利于代码的维护
    2. react中数据渲染页面,UI = f(data),但是尽量把获取和管理数据与界面渲染分开,即把获取数据与管理数据的逻辑放在父组件,把渲染界面的逻辑放在子组件
    3. A组件渲染B组件时,即使props传入一样,B组件也会完整走一遍渲染流程
    4. 函数形式的react组件,好处是不需要管理state,占用资源少,但是函数组件无法利用shouldComponentUpdate来优化减少渲染
    5. PureComponent中实现的shouldComponentUpdate会对传入的参数进行浅比较,当传入的props为一个对象之类的,比如由{a: 1, b: 2} => {a: 2, b: 2},可能会出现UI未更新的情况出现
    6. shouldComponentUpdate函数,每次渲染render函数之前会被调用,返回true继续渲染,返回false立刻停止,可以对深层次的数据处理
    7. 在使用PureComponent时,尽量在render定义之外创建所有对象,数组和函数,并确保在各种调用间,不发生更改 - 参考table例子
    8. React.memo浅比较
    
      export default class A extends React.Component {
        state = {
          joke: null
        }
    
        componentDidMount() {
          fetch(url, {header: {'Accept': 'application/json'}})
            .then(res => {
              return res.json()
            })
            .then(json => {
              this.setState({joke: json.joke})
            })
        }
    
        render() {
          return <B value={this.state.joke} />
        }
      }
    
      export default class B extends React.Component {
        render() {
          return (
            <div>
              <img src={url} />
              {this.props.value || 'loading...'}
            </div>
          )
        }
      }
    
      <Table
        // map每次返回一个新数组,浅比较失败
        rows={rows.map()}
        // 枚举对象总不相同 - 尽量不写行内样式
        style={{color: '#ccc'}}
        // 箭头函数每次都会重新渲染 - 组件层级不通过箭头函数绑定事件
        onUpdate={() => {}}
      />
    

    高阶组件

    待补充...

    render props模式

    1. render props的形式
    2. render props其实就是依赖注入
    3. 如何利用render props实现共享组件之间的逻辑
    4. 依赖注入:**当逻辑A依赖逻辑B时,如果让A直接依赖B,A做不到通用。依赖注入就是把B的逻辑以函数形式传递给A,让A和B之间只需要对函数接口达成一致 - 组件A和B之间,不是把B组件写在A组件中,而是通过把B组件当参数传到A组件当中**
    
      // 用户登录与未登录情况下显示不同的组件
      const Auth = (props) => {
        const userName = getUserName()
        if(userName) {
          const allProps = {userName, ...props}
          return (
            <React.Fragment>
              {props.login(allProps)}
            </React.Fragment>
          )
        } else {
          return (
            <React.Fragment>
              {props.noLogin(props)}
            </React.Fragment>
          )
        }
      }
    
      <Auth
        login={({userName}) => <h1>Hello {userName}</h1>}
        noLogin={() => <h1>Please login</h1>}
      />
    

    提供者模式

    1. 提供者模式,为了**解决组件间跨层级的信息传递**,A-B-C-D-...-X,当数据要从A传递到X时,通过props传递时,会经过B-C-D...,然而在B-C-D...中不需要使用props中的数据,而且组件在变动时,容易出现props传递错误的情况出现
    2. 提供者模式,本质由2个角色组成,一个叫‘提供者’,一个叫‘消费者’,提供者(A)位于组件树靠上的位置,消费者(X)处于靠下的位置
    3. 实现方式,通过**react提供的context功能进行实现(v16.3.0+),context功能可以创造一个‘上下文’,在这个上下文之中的所有组件都可以访问到同样的数据**
    4. 参考react-app中x2代码
    

    组合组件

    1. 模式(Pattern) = 问题场景(Context) + 解决方法(Solution)
    2. 解决的问题,**父组件想传递一些信息给子组件,但是通过props传递又很麻烦时**
    

    组件状态

    1. UI = f(data),data = props + state,props代表外部传进来的数据,state代表组件内部状态
    2. 什么样的数据可以存放在成员变量中?
    3. 如果数据由外部传入,放在props中
    4. **如果是内部组件数据,这个数据的更改是否应该立刻引发一次组件的重新渲染,如果是,放在state中,不是就放在成员变量中**
    5. state不会被同步修改,在react的生命周期中或者处理函数中同步调用,setState不会立即同步state和重复渲染,但是如果setState由其他条件引发,就有可能不是这样了 - 在生命周期中或者事件函数中调用时,react会打开一个类似标记的东西,标记打开的过程中,setState调用都是往任务队列里放任务,当函数调用结束时,批量处理任务队列,关闭标记
    6. 函数式setState -  当setState的第一个参数为函数时,任务列表上增加的就是一个可执行的任务函数,react没处理完一个任务,都会更新一次state,然后把新的state传递给这个任务函数
    
      // 代码1
      class Foo extends Component {
        foo = 'foo'
    
        render() {
          return <div>{this.foo}</>
        }
      }
      // 代码2
      this.state = {
        count: 0
      }
    
      this.setState({count: 1})
      console.log(this.state.count) // 0
      // 代码3
      setTimeout(() => {
        this.setState({count: 2})
        console.log(this.state.count) // 2
      })
      // 代码4 - 结果为1 三个任务都给了this.state.count一个值
      this.state = {
        count: 0
      }
      this.setState({count: this.state.count + 1})
      this.setState({count: this.state.count + 1})
      this.setState({count: this.state.count + 1})
      // 代码5 - 函数式setState,结果为3
      // this.setState((preState, props) => ({})
      function increment(state, props) {
        return {count: state.count + 1}
      }
      this.state = {
        count: 0
      }
      this.setState(increment)
      this.setState(increment)
      this.setState(increment)
    

    redux使用模式

    1. 使用场景 - 复杂应用下的状态维护,类似于一个全局的store,**并且store只有接受某些‘事件’(action),才可以修改store上的数据,store对这些‘事件’的响应,就是修改状态(修改状态的函数reducer)**
    2. 事件(action) - 响应函数(reducer) -store
    3. 对于某个状态,是放在store中还是组件自身状态中?
    4. 看状态是否会被多个react组件共享 - 不同级组件件的数据共享
    5. 看这个组件被unmount之后重新被mount,之前的状态是否需要保留 - 组件销毁后又重新渲染,之前的输入是否要保留
    6. 代码组织方式 - 基于角色分类(modal,services,view,action,reducer)和基于功能分类(安装功能所在模块分类)
    7. connect函数接受两个参数,一个mapStateToProps是把Store上的state映射为props,另一个mapDispatchToProps是把回调函数类型的props映射为派发action,connect函数调用会产生一个‘高阶组件’
    8. react-redux中使用了三个模式 - 提供者模式,高阶组件,有状态组件和无状态组件
    
      // 代码1
      import { createStore } from 'redux'
      import { Provider } from 'react-redux'
      import store from './store'
    
      const store = createStore(store)
    
      <Provider store={store}>
        {Provider之下的所有组件都可以connect到给定的store}
      </Provider>
      // 代码2
      const Button = ({count, onIncrement}) => {
        return (
          <div>
            <div>{count}</div>
            <button onClick={onIncrement}>+</button>
          </div>
        )
      }
      // 代码3
      import { connect } from 'react-redux'
    
      const mapStateToProps = (state) => {
        return {
          count: count + 1
        }
      }
    
      const mapDispatchToProps = (dispatch) => {
        onIncrement: () => dispatch({type: 'INCREMENT'})
      }
    
      const Counter = connect(mapStateToProps, mapDispatchToProps)(Button)
    

    mobx使用模式

    待补充...

    redux与mobx模式对比

    待补充...

    路由 react router

    1. 单页应用 - 把URL映射到对应的页面来处理,页面之间切换做到局部更新
    2. 动态路由 - 指的路由规则不是预先确定的,而是在渲染过程中确定的(React Router v4)
    3. 静态路由 - 路由规则是固定的
    

    未完待续...

  • 相关阅读:
    R 读取xls/xlsx文件
    网页免费转换为可编辑的PDF
    Python: NumPy, Pandas学习资料
    鱼油资料
    Activity的四种启动模式和onNewIntent()
    Android Service、IntentService,Service和组件间通信
    Activity生命周期
    Node.js学习起步
    Android 技能图谱学习路线
    Blog
  • 原文地址:https://www.cnblogs.com/sk-3/p/12903395.html
Copyright © 2020-2023  润新知