复习React笔记。
1、react核心概念要点
1)JSX 防止注入攻击 可以安全地在 JSX 当中插入用户输入内容: React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。
2)不要在render里面修改react组件的props,会造成反复渲染。
3)state 的更新可能是异步的 出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。
1 |
|
以上写法可以完美避免this.state.counter在被多次set以至于没有得到开发者想要的结果。
4)react数据流 React的数据流是向下流动的,render的渲染时自内向外的。
5)阻止默认事件和冒泡 React 中不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault。阻止冒泡也不能使用cancelBubble = true;必须调用stopPropagation。 原因是:react的所有事件都绑定在页面根节点上,react提供了自己的一套事件处理机制,自己做好了事件的冒泡和捕获。react只能通过它预留出来的方法进行阻止默认事件或冒泡,恰好react保留的是preventDefault和stopPropagation方法。 具体的react合成事件对象参照:合成事件 详细原理参考:React 中阻止事件冒泡的问题
6)事件绑定写法
1 |
|
写法优于
1 |
|
此语法问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。浪费内存资源。
7)事件传参的两种写法
1 |
|
8)非受控组件 是非受控组件。 它的value只读,value不受state控制,也没有onChange事件监听。
9)状态提升 将state放在父级组件,使其子组件得以共享数据源。
10)props.children props.children是一个特定关键字,预留了react组件的子组件,如:
1 |
|
其中select的props.children就是option。
11)关于react组件继承 我们在成百上千个组件中使用 React。我们并没有发现需要使用继承来构建组件层次的情况。一般情况下wooden自定义组件不需要互相继承,更多是通过extend继承React.Component。 最主要原因是由于组件之间的state和生命周期不能共享,需要单独维护。
12)创建react项目步骤:
- 执行命令sudo npm install -g create-react-app 下载react项目构建工具‘create-react-app’
- 执行命令create-react-app tianyuan-react-app 构建名为‘tianyuan-react-app’的react项目
- 执行命令 cd tianyuan-react-app 进入新构建的项目中
- 执行命令 npm install redux –save 安装redux(没加-dev,因此安装的redux在package.json的dependencies里面)
- 执行npm run eject查看当前项目的webpack配置文件。 执行之后,会拷贝一份项目默认的webpack配置到本地,生成一个同src同级的config文件夹。因此,开发者再去修改webpack配置时,将不必修改项目默认的webpack配置,因为此时项目是直接采用config文件夹里面的配置,修改config文件夹里面的配置即可。
- 执行命令npm install react-redux –save安装react-redux工具。
2、React进阶
1)context
1 |
|
App.js代码如下:
1 |
|
注意: 因为 context 会根据引用标识来决定何时进行渲染(本质上是 value 属性值的浅比较),所以这里可能存在一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为 value 属性总是被赋值为新的对象。为了防止这种情况,将 value 状态提升到父节点的 state 里。
context实现原理参考:https://juejin.cn/post/6844904175294251022
2)refs 下面是几个适合使用 refs 的情况:
- 管理焦点,文本选择或媒体播放。
- 触发强制动画。
- 集成第三方 DOM 库。
ref属性的特点: 当 ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性。 当 ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其 current 属性。 不能在函数组件上使用 ref 属性,因为他们没有实例。 如果要在函数组件中使用 ref,你可以使用 forwardRef(可与 useImperativeHandle 结合使用),或者可以将该组件转化为 class 组件。
React 会在组件挂载时给 current 属性传入 DOM 元素,并在组件卸载时传入 null 值。ref 会在 componentDidMount 或 componentDidUpdate 生命周期钩子触发前更新。
1 |
|
React提供的 Ref forwarding 方案用来将 ref 通过组件传递给其子节点。这种场景对于可复用组件库和高阶组件很有用。
也就是说,可以使用 React.forwardRef 函数将 ref 转发到组件中,Ref forwarding 允许组件接收一个 ref,并将它向下传递 / 转发(用来点题)给子组件。
1 |
|
以上代码做了如下操作:
- 使用 React.createRef() 创建一个 ref 实例(inputRef);
- 将 ref 实例指定给组件(TextInput)的 ref 属性 ;
- React.forwardRef()方法将 ref 实例作为其回调函数的第二个参数向下转发给底层DOM组件();
- 在外层组件中能够通过 current对象访问DOM节点值。
以上代码,可以认为ref通过两层传递,从CustomTextInput组件传到input,最终获取到了input元素。
回调ref
回调refs的作用是在 componentDidMount 或 componentDidUpdate 触发前,保证 refs 一定是最新的实体元素。
1 |
|
可参考https://zhuanlan.zhihu.com/p/110562899
3)fragments
React.Fragment 就是 <></>。
区别是可以带key。
3、React16新的生命周期
mount阶段:constructor ->getDerivedStateFromProps->render->componentDidMount
更新阶段:getDerivedStateFromProps->shouldComponentUpdate->render->componentDidUpdate
卸载:componentWillUnmount
生命周期调整原因:React改成fiber架构之后,那三个will生命周期,可能会多次重复执行。
去掉了三个will: componentWillMount、componentWillReceiveProps、componentWillUpdate
Demo:
1 |
|
16.8的重大更新 fiber 生命周期 hooks
4、React16新的特性
5、React中组件传递数据的方式, 父子,子父,兄弟
6、React中列表元素为什么要加key属性
React中的列表组件需要加key属性,不加的话在本地控制台会显示一个警告:“Warning: Each child in a list should have a unique “key” prop”。而生产环境不会。
下面我们来深入了解一下key:
- key属性的作用
1)由于React是利用模板引擎对页面进行渲染,新旧Dom树进行对比后再转化成真实的Dom树。每当有props或者state的数据发生改变时,都会触发render,返回一个新的组件树。
2)如果一个组件内部特别复杂的时候,新旧组件树的对比将会非常消耗性能,算法复杂度能达到O(n)3,显然是不能被接受的。
3)因此为了解决此性能问题,提出了一种启发式算法,将新旧Dom对比转化成真实Dom的过程的算法复杂度降低到O(n)。具体如下:
这种算法将对React组件进行假设和预判。
假设是指页面元素的类型不同,将会渲染出完全不同的树。
预判是任意一个父组件的每一次渲染,同一个 key 属性的子组件应该是稳定的(每一次父组件执行 render() 方法的时候,同一个 key 属性值永远标识唯一的 React 组件)。
4)使用key属性的好处:由于React启发式算法中将key属性作为组件唯一标识的规则,无论是从哪个位置插入新元素, React在下次渲染的时候,仍然能够识别这些组件,不会对原来的旧组件执行变更操作。
5)key属性遵循的原则:唯一、稳定。
- 如果没有key属性,会引发什么样的问题
在没有为组件设置 key 属性的情况下,React 会默认将数组索引设置为 key 属性的值。
数组索引赋值给key的问题:比如选中的数据更改排序,那么选中的数据就发生了变化。
- 如何声明key
因此大多数时候用一个唯一且稳定的标识来给key赋值,一般考虑用API返回的id来确定key的值。
- eslint中与key有关的规则
eslint-plugin-React 中
1 |
|
7、React中的高阶组件(HOC)
高阶组件是参数为组件,返回值为新组件的函数。 不是react的语法,是基于 React 的组合特性而形成的设计模式。
HOC 在 React 的第三方库中很常见,例如 Redux 的 connect 和 Relay 的 createFragmentContainer。
不要试图在 HOC 中修改组件原型,我们只是给原有的组件增添一些功能。可以将 HOC 视为参数化容器组件。
HOC 还可以传递从外界传入的props。大多数 HOC 都可以包含一个类似于下面的 render 方法:
1 |
|
这种约定保证了 HOC 的灵活性以及可复用性。
HOC的典型应用:connect
1 |
|
等同于
1 |
|
connect 是一个返回高阶组件的高阶函数!
注意,不要在 render 方法中使用 HOC!看下面的例子:
1 |
|
这种操作相当于在render里面修改state中的数据一样,导致页面反复渲染。
ref不在props里面,一般传递props的时候,ref传递不到组件里面。
高阶组件最大的缺点是:无法继承静态属性(static)。 这些静态属性被高阶组件包裹一层之后,无法在传递进来的该组件中使用。除非用高阶组件中再添加一个静态方法去手动调用该组件的静态属性。代码如下:
1 |
|
或者通过使用 hoist-non-react-statics 自动拷贝所有非 React 静态方法。
高阶组件demo:
1 |
|
8、React中函数组件和class组件的区别
1)class组件特点:
- 有组件实例;
- 有生命周期;
- 有 state 和 setState。
2)函数组件特点:
- 没有组件实例;
- 没有生命周期;
- 没有 state 和 setState,只能接收 props;
- 函数组件是一个纯函数,执行完即销毁,无法存储 state。
class 组件存在的问题:
- 大型组件很难拆分和重构,变得难以测试;
- 相同业务逻辑分散到各个方法中,可能会变得混乱;
- 复用逻辑可能变得复杂,如 HOC 、Render Props。
所以 react 中更提倡函数式编程,因为函数更灵活,更易拆分,但函数组件太简单,所以出现了hook,hook就是用来增强函数组件功能的。
在react16.8版本中添加了hooks,使得我们可以在函数组件中使用useState钩子去管理state,使用useEffect钩子去使用生命周期函数。因此,2、3两点就不是它们的区别点。从这个改版中我们可以看出作者更加看重函数组件,而且react团队曾提及到在react之后的版本将会对函数组件的性能方面进行提升。