React 开发笔记

1.插槽 { this.props.children }

2.IIFE

<div>{(function () { return 'is div'})()}</div>

3.生命周期

组件创建阶段:一辈子只执行一次

componentWillMount【过时】

render

componentDidMount

组件运行阶段:根据 props 或 state 改变,有选择性地执行 0 到多次

componentWillReceiveProps

shouldComponentUpdate

componentWillUpdate

render

componentDidUpdate

组件销毁阶段:一辈子只执行一次

componentWillUnmount

4. Concurrent Mode 可中断渲染:React 不需要阻塞浏览器更新文本输入,相反,它可以让浏览器绘制输入的更新,然后在内存中渲染更新后的列表,当渲染完成后,React 更新 DOM。

  React 在 Concurrent 模式中可以中断一项正在执行的更新去做一些更重要的事情,然后再回到之前正在做的工作。

  比如中断渲染,继续更新输入框,避免卡顿

  在旧屏以合理的 loading 提示等待,在内存中完成新屏的加载和渲染内容,无缝切换,跳过空白。

5. 根据官方文档《不可变数据的力量 immutability》总结

  • 在使用 this.setState 时,数组的赋值需要重新定义一个新的数组,使用 concat 等,对象的赋值同理使用 Object.assign({}, ...obj) 等,或者使用 ES6 的扩展运算符。
  • PS:在 hook 中我们推荐把 state 切分成多个 state 变量,并永远以非合并的方式去替换,见例子0.5,为抽取自定义 hook 提供便利
  • 始终保持原对象,返回新对象,当处理深层嵌套对象时,请参阅 Immerimmutability-helper
例子0.5:
// 可以但不推荐 const [state, setState] = useState({ left: 0, top: 0, width: 100, height: 100 }) setState(state => ({ ...state, left: e.pageX, top: e.pageY })); // 推荐,注意替换的方式,一个新的无关旧 state 的 {} const [position, setPosition] = useState({ left: 0, top: 0 }); const [size, setSize] = useState({ width: 100, height: 100 }); setPosition({ left: e.pageX, top: e.pageY });

6 ?. 单向数据流:子组件不能直接修改父组件的 state 数据?

  • 组件本身无法知道它是来自于 Clock 的 state,或是 Clock 的 props,还是手动输入的

7. state 异步更新:千万不要用一个 state 属性去直接计算另一个 state 属性

// Correct
// 出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。
// 因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态
// 这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

8. 了解 static-getderivedstatefromprops

9 ?.如何避免重渲染

  • 子组件 shouldComponentUpdate(nextProps, nextState)
  • componentDidUpdate(prevProps, prevState) 比较 prevState.count 和 this.state.count 是否相等
  • useEffect(cb, [state/props]可选) 不写默认更新所有状态,若传一个空 [],则表示只在初次渲染和卸载销毁时作用(注意:前提是 useEffect 不涉及 state/props),但不影响非 useEffect 等方法的执行,有时,一个空 [] 会有隐患,如例子0.9
  • useMemo 在组件渲染 过程中 执行
例子 0.9
function Example({ someProp }) { useEffect(() => { function doSomething() { // 假如 doSomething 定义在 useEffect 外部,[someProp] -> [] 则不安全 console.log(someProp); } doSomething(); }, [someProp]); // ✅ 安全(effect 内部声明函数,我们的 effect 仅用到了 `someProp`) }

10. 插件

  • 快捷编程 simple react snippets
  • prop 数据校验 prop=types
  • 动画库 react-transition-group
  • 无限滚动 react-virtualized
  • 没有嵌套关系的组件之间通信 events
  • antd
  • redux
  • 简化 redux 流程: react-redux + redux
  • 中间件向后台请求数据(redux 纯函数禁止异步请求的解决方案): redux-thunk
  • 功能同上,替代品: redux-saga
  • react-router-dom
  • 检测 hook,同时需要配置 .eslintrc.js: eslint-plugin-react-hooks --dev
  • swr 异步请求,请求重复数据删除,缓存数据,乐观更新,TS 支持……
  • 懒加载组件,代码分割 @loadable/component
  • json 显示器 react-json-view
  • 富文本编辑器 react-quill
  • 拾色器 rc-color-picker
  • 二维码生成 qrcode.react
  • 数据可视化 antV
  • 拖拽库 react-beautiful-dnd
  • 表情库 emoji-mart
  • 图片裁切 react-image-crop
  • 动画 ant motion
  • 代码编辑器 react-codemirror2
  • 复制到剪切板 react-copy-to-clipboard

11. 最近复习遇到的所有问题,待解决:

  • ES6的模块引入是静态分析的,故而可以在编译时正确判断到底加载了什么代码
  • 继承 React.Component.prototype.constructor.call(this) // this class 的实例对象
  • static propTypes ?
  • useEffect,useMemo 缓存,useCallback 缓存的区别
  • hook 版是否可替代 redux 呢?:useContext+useReducer
  • fiber

12. 跨层级组件通信 Context Api,不推荐

// context.js
import React from 'react'
let { Consumer, Provider } = React.createContext()
export {
  Consumer,
  Provider
}

12 new: 组件通信 eventEmitter

  • useContext 解决组件嵌套太深的问题
  • Provider,Consumer,Context

13. renderProps 的妙用

// title: 追逐鼠标的猫
// tips:['render'] 关键字可以自定义,因为 render props 是一种模式,而具体怎么命名是自由的

// class Cat
return (<img src="/cat.jpg" absolute', left: mouse.x, top: mouse.y }} />)

// 核心组件或者可以称之为动态组件,动态 render
// class Mouse + prop 的函数校验
return (<div>{this.props.render(this.state)}</div>)

Mouse.propTypes = {
  render: PropTypes.func.isRequired
};

// render prop 是一个用于告知组件需要渲染什么内容的函数 prop
// 父组件 class MouseTracker
return (<Mouse render={mouse => (<Cat mouse={mouse} />)}/>)

// 父组件另一种写法,prop 并不真正需要添加到 “attributes” 列表中
return (<Mouse ['attributes']>{mouse => (<p>鼠标的位置是 {mouse.x},{mouse.y}</p>)}</Mouse>)

14. redux 流程

15. hooks 闭包机制

  • 每次渲染都会生成一个新的 effect,这正是获取最新 state 而不用担心过期的原因。
  • useEffect 可以完成副作用操作:DOM 操作,数据请求,组件更新,订阅
  • 与 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕
  • useEffect 的清除阶段 return fn 有别于组件卸载阶段,每次渲染更新都会清除(总是把握实时的状态,清除时不会出错),不易造成内存泄漏或崩溃;而假如你还在使用 class 组件编程,当你 忘记 在 componentDidUpdate 中 先清除再订阅 时,这正是应用中常见的 bug 来源!
  • hook 用于复用状态逻辑,便于单独测试,解决复杂组件充斥复杂逻辑和副作用的问题。
  • 带有 hook 的无状态组件称之为'函数组件'

16. 获取 dom

  • useRef(null) 获取子组件 dom
  • forwardRef((props, childRef) => { return (...) }) 获取子组件中某个元素的 dom
  • ref
  • createRef