学一门框架,首先要熟悉官方文档,不然框架总会学得有所欠缺。尤其当使用过React一段时候后,对此深有体会。于是便依据官方文档做了以下的学习笔记。建议大家也去官方文档学习,毕竟每个人对文档的理解都有所不同。
作用:提供了一个无需为每层组件手动添加props,就能在组件树中进行 自上而下 数据传递的方法。 API:React.createContext() 1、使用React.createContext()创建“上下文组件” 2、在组件树的任何节点上包裹一个
注意:Provider 接受的value会传递给消费者组件,多个Provider 可以嵌套使用,里层的会覆盖外层的value数据。当Provider的value值发生改变时,其内部的所有消费者组件都会被重新渲染,Provider及其内部的消费者组件(consumer)都不会被shouldComponentUpdate 函数控制。 3、在后代组件节点上,使用 contextType属性 或者 使用Consumer使用上下文数据。 注意事项:当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。为了防止这种情况,将 value 状态提升到父节点的 state 里,即(状态提升)。
在vue中当我们需要对页面DOM进行操作时,我们会用到ref。作为vue的前辈,React也会ref操作DOM的API。React.createRef()这个API可以在React中创建一个DOM元素的ref。 当然React不建议我们使用ref获取DOM元素 例如:以class组件为例
class CustomRef extends React.Component {
constructor(props) {
super(props);
// 创造 DOM 元素的 ref
this.Refs = React.createRef();
}
render() {
// 使用 `ref` 回调函数以在实例的一个变量中存储文本输入 DOM 元素
//(比如,this.Refs)。
return (
{console.log( this.Refs.current)} }
/>
);
}
}
:refs不会被透传下去,因为ref不是props属性,与key一样会被React进行特殊处理
React 中用于复用组件逻辑的一种高级技巧。高阶组件是参数为组件,返回值为新组件的函数。由于React16.8以后有了Hook ,所以一般直接使用函数组件即可。
//写一个装饰HOC函数 hoc/test.jsx import React from "react"; export default C => () => { return ( <>
这是装饰的头部
这是装饰的尾部> ) //再写一个需要装饰的函数组件 view/T.jsx import React from "react"; import test from "../hoc/test"; export default test(()=>{ return这是要被修饰的组件}) // 再到入口文件中进行展示 import React from 'react'; import ReactDOM from 'react-dom'; import T from './views/T'; ReactDOM.render(, document.getElementById('root') ); 除了上面的一种写法
ES7的装饰器也可以用来进行处理
例如之前的 T.jsx文件还可以这么写
import React from "react"; import test from "../hoc/test"; // export default test(()=>{ // return
这是要被修饰的组件// }) @test class T extends React.PureComponent{ render(){ return(这是要被修饰的组件) } } export default T因为装饰器是一个实验性的 JavaScript 提案。所以我需要安装对应的babel进行处理。
注意:
1.不要再render方法中使用HOC,因为每次调用render时,都会导致子组件被重新渲染,这不仅仅消耗性能,而且重现挂载组件还会导致该组件及其所有子组件状态的丢失。
2.静态方法需要复制,React文档中有提到,当使用HOC应用于组件时,原始的组件将会被使用容器进行包装。这会导致新组建中没有原始组件的任何静态方法。所以我们需要在其放回之前将这些方法拷贝到容器组件上。React官方推荐我们使用 hoist-non-react-statics 自动拷贝所有非 React 静态方法:
import hoistNonReactStatic from 'hoist-non-react-statics'; function enhance(WrappedComponent) { class Enhance extends React.Component {/*...*/} hoistNonReactStatic(Enhance, WrappedComponent); return Enhance; }
3.Refs不会被传递, 原因很简单,ref与key一样会被React在编译过程中被处理。官方也给出了解决方案使用
React.forwardRef
这个API。4.props属性需要继承,在封装高阶组件时以防属性的丢失,我们一定要添加属性的继承。
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。
Hook API的操作直达React底层,所以性能更好、效率更高、代码也更加简洁。
使用class组件会带来一些问题,例如:class不能很好的被压缩,并且热重载时不太稳定。基于这些问题,React建议我们使用Hook,因为Hook能是代码更易于优化。
我们使用Hook API可以模拟出class组件大多数的特性,比如 state、生命周期、上下文、ref等。
Hook常用的几个API有useState, useEffect, useMemo, useContext, useCallback
这是一种在函数调用时保存变量的方式 ——
useState
是一种新方法,它与 class 里面的this.state
提供的功能完全相同。一般来说,在函数退出后变量就会”消失”,而 state 中的变量会被 React 保留。useState() 唯一的参数就是存放初始的state值。其state值的类型 可以是数字、字符串或者对象等。
setState React 会在重复渲染时记住它当前的值,并且提供最新的值给我们的函数。我们可以通过调用 setState 来更新当前的
state值
。
import React, { useState } from 'react';
export default () => {
//useState用来声明一个变量 这个初始出来的state参数只在第一次渲染时会被被创建
//其中 num为被声明的变量名,我们可以通过调用setNum来修改这个变量
const [num, setNum] = useState(123);
return (
// 这个为React的随便化语法
{num}
setNum(num+1)}>自增
)
}
建议:将 state 变量声明为一对
[something, setSomething]更
方便在开发中查找使用。为什么使用“数组解构”?因为这样能利用数组位置进行赋值操作。
当需要进行一些有阻碍js的操作或性能消耗的操作时,我们可以使用useEffect来对这些“副作用”进行处理。
我们可以把 useEffect Hook 看做
componentDidMount
,componentDidUpdate
和componentWillUnmount
这三个函数的组合。为什么在组件内部调用
useEffect
?将
useEffect
放在组件内部让我们可以在 effect 中直接访问 state 变量(或其他 props)
useEffect
会在每次渲染后都执行吗?是的,默认情况下,它在第一次渲染之后和每次更新之后都会执行。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。
注意:使用
useEffect
调度的 effect 不会阻塞浏览器更新屏幕。(异步操作)代码示例:
useEffect(()=>{ // 这里相当于componentDidMount() let time = setInterval(()=>{ console.log(Date.now()) },1000) return ()=>{ //这里相当于componentWillUnmount() // 清除函数,用来操作一些需要清除的数据,例如:组件销毁后清除定时器 clearInterval(time); time = null; console.log("组件要卸载了"); } },[])
useEffect的用法:两个参数:第一个参数中 是需要进行的操作和一个return ,在return中存放清除这个操作的影响,例如使用了定时器,当我们销毁这个组件时,我们就需要清除定时器对全局的影响,第二个参数: 触发条件,当第二个参数为空时,每当页面需要重新渲染时,useEffect都会被调用执行;当第二个参数为空数组时,useEffect只会在组件创建时执行一次,会通知 React 跳过对 effect 的调用;当第二个参数为数组(可以有多个元素)时,会监听其中的参数是否发生改变,当参数发生改变就会通知 React对 effect 的调用。
const value = useContext(MyContext);
接收一个 context 对象(
React.createContext
的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的<MyContext.Provider>
的value
prop 决定。当组件上层最近的
更新时,该 Hook 会触发重渲染,并使用最新传递给
MyContext
provider 的 contextvalue
值。即使祖先使用React.memo 或shouldComponentUpdate ,也会在组件本身使用useContext
时重新渲染。const themes = { light: { foreground: "#000000", background: "#eeeeee" }, dark: { foreground: "#ffffff", background: "#222222" } }; const ThemeContext = React.createContext(themes.light); function App() { return (
); } function Toolbar(props) { return ( ); } function ThemedButton() { const theme = useContext(ThemeContext); return ( ); }
只在最顶层使用 Hook,不要在循环,条件或嵌套函数中调用 Hook。确保Hook总会在函数最顶层被调用,以确保Hook在每一次渲染时都被会被按照同样的顺序被调用。
ESLint插件 :
eslint-plugin-react-hooks
npm install eslint-plugin-react-hooks --save-dev
// 你的 ESLint 配置 { "plugins": [ // ... "react-hooks" ], "rules": { // ... "react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则 "react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖 } }
自定义Hook一般是“use**”的格式命名,我们可以通过封装一个符合自定义规则命名的Hook调用其他的Hook,Hook函数内部可以调用其他的 Hook。
当然,如果不想造轮子的话,我们可以使用 react-use 库,该库封装了需要我们常用到的一些方法。
自定义 Hook 必须以 “
use
” 开头,不遵循的话,由于无法判断某个函数是否包含对其内部 Hook 的调用,React 将无法自动检查你的 Hook 是否违反了Hook规则。在两个组件中使用相同的 Hook 不会共享 state,Hook是一种重用状态逻辑的机制,所以在不同组件的 Hook中的所有state和副作用都是完全隔离的。