深入浅出react&redux笔记

| 字数 1918
  1. 1. 深入浅出react&redux笔记
  2. 2. tips
  1. 1. 深入浅出react&redux笔记
  2. 2. tips

深入浅出react&redux笔记

  1. 高内聚(js、jsx、css) 低耦合(弱化不同组件间的关系)
  2. react组件数据

    1. propTypes 建议在开发环境可加,生产环境通过babel-react-optimize去掉
    2. 初始化state:

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        this.state = {
        count: props.initValue || 0
        }
        // =>
        this.state = {
        count: props.initValue
        }
        xxx.defaultProps = {
        count: 0
        }
    3. prop&state的对比:

      1. prop 用于定义外部接口, state 用于记录内部状态;
      2. prop 的赋值在外部世界使用组件时, state 的赋值在组件内部;
      3. 组件绝不应该改变 prop 的值(要严格遵守单向数据流原则,保证 ui=render(data) 这个函数无任何副作用),而 state存在的目的就是让组件来改变的 。

    4. 生命周期 (执行顺序如序号顺序)

      • 装载过程
        1. constructor (初始化state,绑定this。当然也可以不绑定,在class中使用声明变量&尖头函数的写法)
        2. getlnitialState (React.createClass中使用)
        3. getDefaultProps (React.createClass中使用)
        4. componentWillMount (这个时候没有任何渲染出来的结果,即使调用this.setState 修改状态也不会引发重新绘制换句话说,所有可以在这个 component­WillMount 中做的事情,都可以提前到 constructor 中间去做)
        5. render (完全根据 this.state 和 this.props 来决定返回的结果,而且不要产生任何副作用。在 render 函数中去调用 this.setState 毫无疑问是错误的,因为一个纯函数不应该引起状态的改变)
        6. componentDidMount (组件已经被装载到 DOM 树上了,这个时候我们可以通过请求来填充组件的内容、使用三方代码获取dom, 需要注意的是该生命周期不会在一个render后立即执行,而是在所有子组件render结束后执行,这是因为render只返回jsx对象并不往dom树上装载内容,具体的内容装载是react经过vm后才决定的)
      • 更新过程
        1. shouldComponentUpdate (如果这个函数返回 true,那就会继续更新过程,接下来调用 render 函数;反之,如果得到 一个 false,那就立刻停止更新过程,也就不会引发后续的渲染了)
        2. componentWillReceiveProps (只要是父组件的 render 函数被调用,在 render 函数里面被谊染的子组件就会经历更新过程,不管父组件传给子组件的 props 有没有改变,都会触发子组件的 componentWillReceiveProps 函数, 子组件中的 setstate 不会调用该方法)
        3. componentWillUpdate
        4. render
        5. componentDidUpdate
      • 卸载过程
        1. componentWillUnmount (通常解绑事件、定时器等)
  3. 模块化
    用 React和 Redux来构建前端网页应用,这两者都奉 行这样一个公式 UI=render(state) 来产生用户界面。 React才适合于视图层面的东西,但是不能指望靠React来管理应用的状态, Redux才适合担当应用状态的管理工作。

    • 从架构出发,当我们开始一个新的应用的时候,有几件事情是一定要考虑清楚的:

      1. 代码文件的组织结构

        • 按角色组织:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          reducers/
          todoReducer.js
          filterReducer.js
          actions/
          todoActions.js
          filterActions.js
          components/
          todoList.js
          todoItem.js
          filter.js
          containers/
          todoListContainer.js
          todoiternContainer.js
          filterContainer.js

          // 唯一的缺点是新增功能需要在不同的文件夹下切换并且创建新文件
        • 接功能组织:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          todoList/
          actions.js
          actionTypes.js
          index.js
          reducer.js
          views/
          component.js
          container.js
          filter/
          actions.js
          actionTypes.js
          index.js
          reducer.js
          views/
          component.js
          container.js
      2. 模块接口

        • 常规导入:

          1
          2
          3
          4
          import * as actions from ’ .. /todoList/actions ’;
          import container as TodoList from ’../todoList/views/container’;

          // 这种写法当然能够完成功能,但是却非常不合理,因为这让 filter模块依赖于 todoList模块的内部结构,而且直接伸手到 todoList 内部去导人想要的部分
        • 统一入口导入 (index.js)

          1
          2
          3
          4
          5
          import * as actions from ’. /actions.js ’;
          import reducer from ’. /reducer.js’;
          import view from ’./views/container.js ’;

          export {actions, reducer, view};
          1
          2
          // 使用如下
          import {actions , reducer, view as TodoList} from ’../todoList’
      3. Store 的状态树设计

        • 一个模块控制一个状态节点
        • 避免冗余数据 (数据一致性)
        • 树形结构扁平,避免出现以下情况:
          1
          const d = state.A && state.A.B && state.A.B.C && state.A.B.C.D;
  4. 组件性能优化

    1. 容器组件与傻瓜组件 (让一个组件只专注做一件事,容器组件进行数据处理然后将数据传入傻瓜组件,傻瓜组件只负责渲染)
    2. 使用 shouldcomponentupdate 来避免不必要的组件更新(重新渲染)
    3. 那么如何查看 chrome 中 react 性能表现?参考资料如下:
  5. 高阶组件
    | | 特点 | 区别 |
    | :—–| :—-: | :—-: |
    | 代理方式的高阶组件 | 操纵 prop / 访问 ref / 抽取状态 / 包装组件 | 新组件、参数组件都要经历各自的生命周期 |
    | 继承方式的高阶组件 | 操纵 prop / 操纵生命周期函数 | 只有一个生命周期(super.render()) |

  6. redux

    1. redux基本原则:
      • 唯一数据源(Single Source ofTruth)
      • 保持状态只读( State is read-only)
      • 数据改变只能通过纯函数完成( Changes are made with pure functions)
    2. 保持数据的唯一性 (context & react-redux)
    3. react-redux 两个最主要功能:
      • connect: 连接容器组件和傻瓜组件;
      • Provider: 提供包含 store 的 context (context 完全可以实现 provider 但是 react-redux 中的 provider 包含三个函数:subscribe、dispatch、getState。react-redux 定义了 Provider 的 componentWillReceiveProps 函数,在 React组件的生命周期中, componentWillReceiveProps 函数在每次重新渲染时都会调用到,react­ redux 在 componentWillReceiveProps 函数中会检查这一次渲染时代表 store 的 prop 和上 一次的是否一样。 如果不一样,就会给出警告,这样做是为了避免多次渲染用了不同的 Redux Store。 每个 Redux 应用只能有一个 Redux Store,在整个 Redux 的生命周期中都应该保持 Store 的唯一性)

tips

  1. 在 jsx 用直接把匿名函数赋值的方法,看起来非常简洁而且方便,其实并不是值得提倡的方法。因为每次渲染都会创造一个新的匿名方法对象 ,而且有可能引发子组件不必要的重新渲染。
  2. render 和 shouldComponentUpdate 函数,也是 React 生命周期函数中唯二两个要求有返回结果的函数。 render 函数的返回结果将用于构造 DOM 对象,而 shouldComponent­Update 函数返回一个布尔值,告诉 React 库这个组件在这次更新过程中是否要继续。
  3. 为什么 react 中大多数的调用通常是闭包形式的多层调用? 因为遵从一个函数只做一件事的原则
    1
    connect(mapStateToProps, mapDispatchToProps)(Counter)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action=> {
if (typeof action === "function"){
return action(dispatch , getState , extraArgument);
}
return next(action)
}
}
/** 这是 thunk 的源码实现,只是单纯的判断了 action 类型,具体做了如下操作:
* 1. 调用 dispatch派发出一个新 action对象;
* 2. 调用 getState获得当前ReduxStore上的状态;
* 3. 调用next告诉Redux当前中间件工作完毕,让Redux调用下一个中间件;
* 4. 访问 action对象 action上的所有数据;
*/
  1. 触发 re-render 的方式:

    • setState
    • props 改变
    • forceupdate
  2. fetch认为只要服务器返回一个合法的 HTTP 响应就算成功,就会调用 then提供的回调函数,即使这个HTTP响应的状态码是表示出错了的400或者500。因为 fetch 的这个特点,所以我们在 then 中,要做的第一件事就是检查传人参数 response status 字段,只有 status 是代表成功的 200 的时候才继续,否则以错误处理 。

  3. 组件-action-store-reducers关系图: