react的新特性
context
用于一个组件向下传递变量和方法,这个组件的所有子组件可以拿到传递的变量和方法。context不好处就是让组件失去了独立性变得不纯粹,不能复用,用时需注意要用在独立的组件身上。
createContext
父组件
provider
父组件
consumer
子组件
可使用多层context,一个组件中多层context顺序不重要,provider和consumer的顺序都不重要。
contextType
consumer的使用太过于繁杂,contextType可以解决在只有一个context简化consumer的写法。
子组件
lazy、suspense
lazy
lazy 利用webpack实现异步加载,会生成一个chunk.js来加载组件
import React, { Component, lazy, Suspense } from "react";
// import { connect } from 'react-redux'
// lazy 利用webpack实现异步加载,会生成一个chunk.js来加载组件
// /*webpackChunkName:"about"*/是将加载about的chunk.js重命名
const About = lazy(() => import(/*webpackChunkName:"about"*/ "./page/About"));
/webpackChunkName:"about"/是将加载about的chunk.js重命名
命名before
命名after
suspense
Suspense 可以再lazy异步加载组件时是实现一个加载ui,lazy和suspense一起使用,只使用lazy的话会报错的。
会在about加载过程中,ui渲染fallback中的内容
export class App extends Component {
render() {
return (
<div>
<Suspense fallback={<div>loading</div>}>
<About />
</Suspense>
</div>
);
}
}
Suspense 可以再lazy异步加载组件时是实现一个加载ui,lazy和suspense一起使用,只使用lazy的话会报错的
但是当异步加载组件失败的话并不会渲染suspense的fallback中的LoadingUI,
需要利用react中的ErrorBoundary
ErrorBoundary
它是用来捕获视图加载失败的,利用的是react中一个声明周期方法componentDidCatch
export class App extends Component {
state = {
hasError: false,
};
componentDidCatch() {
this.setState({ hasError: true });
}
render() {
if (this.state.hasError) {
return <div>error了</div>;
}
return (
<div>
<Suspense fallback={<div>loading</div>}>
<About />
</Suspense>
</div>
);
}
}
blockURL之后,about组件加载失败,componentDidCatch将hasError置为true,渲染error了
ErrorBoundary的另一种写法
不使用componentDidCatch而是会用 static getDerivedStateFromError,一旦发生错误,他就会返回一个新的state并合并到state中。
export class App extends Component {
state = {
hasError: false,
};
static getDerivedStateFromError() {
return {
hasError: true,
};
}
render() {
if (this.state.hasError) {
return <div>error了</div>;
}
return (
<div>
<Suspense fallback={<div>loading</div>}>
<About />
</Suspense>
</div>
);
}
}
memo
当父组件重新渲染子组件也会跟着重新渲染,所以当我们不希望子组件做一些不必要的重新渲染动作时。可以使用memo。
子组件使用memo之后只有所依赖其父组件中值发生变化,才会重新渲染子组件,否则子组件不会发生重新渲染。
memo与purComponent有着一样的效果。
ReactHooks
比较与class类组件的好处:
useState
useState是按照次序声明来返回内容的,这也就说明useState要在最顶部声明,而且次序要固定,不然的话视图显示会得到预期之外的效果。而且useState是不可以在任何有逻辑的地方进行定义的(比如判断),不然的话会报错。useState的个数在每一次渲染时顺序要一致。
- state的重新赋值会引起组件的重新渲染。
- 那么 React 怎么知道哪个 state 对应哪个 useState?答案是 React 靠的是 Hook 调用的顺序。这个是为什么hooks写在顶部且不能写在逻辑语句中。
useEffect
useEffect===componentDidMount、componentDidUpdate、componentWillUnmount这三个声明周期,让hooks写法摆脱了类组件中繁琐的生命周期用法。
useEffect是渲染产生的副作用,是在渲染之后执行的。
useMemo
- useMemo和useEffect有着类似的概念,useMemo则是根据依赖来执行函数体中的逻辑,依赖有变化则会执行,依赖没有变化则不会执行。
- useMemo是需要一个返回值的,而他的返回值是直接参与渲染的,因此useMemo的调用时机是在渲染中执行的。
- 而且useMemo会缓存上一次的返回值,如果下一次渲染依赖没有变化,就会使用被缓存的上一次的返回值。
useMemo(()=>{//执行体}) // 每次渲染都执行执行
useMemo(()=>{//执行体},[]) // 只在第一次渲染时执行
useMemo(()=>{//执行体},[依赖]) // 依赖变化则useMemo中函数执行
useCallBack
useCallBack是用来优化组件的不必要的渲染的,他其实是useMemo的一个变形,他返回的是一个函数。
- 当父组件每次渲染都会走一遍所有的函数和变量的生成,所有父组件给子组件传入一个函数时,即使这个函数没有被执行,但子组件也会因为父组件的渲染而渲染。useCallBack就是用来解决这个问题的。
- 执行时期:渲染中执行。
- useCallBack是由缓存的,他缓存的是一个函数。
useCallBack(()=>{},[]) // 只会在第一次渲染时构建,适用于useCallBack的回调中不依赖(使用)任何其他的值时
useCallBack(()=>{},[依赖])// 根据依赖的变化构建函数,适用于useCallback的回调中有依赖的值
useCallBack(fn)===useMemo(()=>fn) // useCallBack是useMemo的变形
父组件
onClick函数依赖于double,double变化则重新构建onClick函数,double无变化则会不会重新构建函数,使用缓存的函数。
export default function App() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2;
}, [count === 3]);
const onClick = useCallback(() => {
console.log("this is onclick"); //
}, [double]);
return (
<div>
count:{count}
<h1>double:{double}</h1>
<Demo onClick={onClick} />
<button
onClick={() => {
setCount(count + 1);
}}
>
add
</button>
</div>
);
}
子组件
子组件使用memo保持组件的干净性,从父组件传来的onClick函数发生变化才会重新渲染,否则不渲染。
import React, { memo } from "react";
function Demo(props: any) {
console.log("demo Render");
return (
<div>
<h1>demo</h1>
</div>
);
}
export default memo(Demo);
注意:useCallBack要和memo结合使用才能更好发挥useCallBack的好处,绝绝子了!
useRef
可以获取上一次的状态
自定义hooks
可以实现状态逻辑的复用
自定义 Hook 必须以 “use” 开头吗?
必须如此。这个约定非常重要。不遵循的话,由于无法判断某个函数是否包含对其内部 Hook 的调用,React 将无法自动检查你的 Hook 是否违反了 Hook 的规则。
在两个组件中使用相同的 Hook 会共享 state 吗?
不会。自定义 Hook 是一种重用状态逻辑的机制(例如设置为订阅并存储当前值),所以每次使用自定义 Hook 时(相当于创建了一个新的对象,占据了的新的地址,所以和其他使用该hook的地方是互不相干的),其中的所有 state 和副作用都是完全隔离的。
\
Hooks使用法则
- 只在顶层使用hooks,不可以在有逻辑的地方使用hooks,因为hooks可能依赖的是调用的次序,如果在有逻辑的地方使用hooks可能会造成每次的渲染周期的hooks顺序不一致,导致渲染出来的次序发生变化。所有为了保证每次的渲染周期中的hooks顺序一致,只能在顶层使用hooks
- 只在自定义hooks和组件中使用hooks
React 是如何把对 Hook 的调用和组件联系起来的?
React 保持对当先渲染中的组件的追踪。多亏了 Hook 规范,我们得知 Hook 只会在 React 组件中被调用(或自定义 Hook —— 同样只会在 React 组件中被调用)。
每个组件内部都有一个「记忆单元格」列表。它们只不过是我们用来存储一些数据的 JavaScript 对象。当你用 useState() 调用一个 Hook 的时候,它会读取当前的单元格(或在首次渲染时将其初始化)(这个单元格就代表当前的这个state),然后把指针移动到下一个。这就是多个 useState() 调用会得到各自独立的本地 state 的原因。
\