# React
# Context
Context提供了一种方式,能够让数据在组件数中传递,而不必一级一级手动传递
# API createContext()
在组件之间的通信的时候,通常我们都是通过Props来进行传递的,父组件传值给子组件,子组件在通过Props接收父组件传过来的值,但是组件之间如果嵌套过深,那么通过Props一层一层传递,显得太多于复杂麻烦;而Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据。
# 通过props传递数据
案例:
import React from 'react';
function AppContext() {
return (
<Parent name='hanzo' />
)
}
function Parent(props) {
return (
<div>
<Child name={props.name} />
</div>
)
}
function Child(props) {
return <div>i am {props.name}</div> // i am hanzo
}
/**
这样传递有一个弊端就是一旦组件嵌套太多,props一级一级向下传递,显得复杂麻烦
*/
# 使用Context
案例:
import React from 'react';
const AppContext = React.createContext(); // 1
function App() {
return (
<AppContext.Provider value='hanzo'> // 2
<Parent />
</AppContext.Provider>
)
}
function Parent() {
return (
<div>
<Child />
</div>
)
}
function Child() {
console.log(AppContext.Consumer);
return (
<AppContext.Consumer> // 3
// 4.
{
(value) => {
return <div>i am {value}</div> // i am hanzo
}
}
</AppContext.Consumer>
)
}
1、
React.createContext
创建一个Context
对象,当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的Provider
中读取到当前的 context 值。2、
Context.Provider
<AppContext.Provider value={值} > <AppContext.Provider>
每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。
Provider 接收一个
value
属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
3、
Context.Provider
- 使用可以订阅到 context 变更
4、
Context.Provider
里面接收一个函数,函数里面有一个value值就是Context.Provider
提供的value值
# 注意:
消费多个Context
官方案例:
// Theme context,默认的 theme 是 “light” 值 const ThemeContext = React.createContext('light'); // 用户登录 context const UserContext = React.createContext({ name: 'Guest', }); class App extends React.Component { render() { const {signedInUser, theme} = this.props; // 提供初始 context 值的 App 组件 return ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser}> <Layout /> </UserContext.Provider> </ThemeContext.Provider> ); } } function Layout() { return ( <div> <Sidebar /> <Content /> </div> ); } // 一个组件可能会消费多个 context function Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext.Consumer> )} </ThemeContext.Consumer> ); }
Context.Provider
的value值可以是对象,数组类型function App() { return ( <AppContext.Provider value={{ name: 'hanzo', age: 36, sex: '男' }}> <Parent /> </AppContext.Provider> ) }
# Lazy、Supense
代码分割是对你的应用进行“懒加载”当前用户所需要的内容,能够显著提高你的应用性能,可以避免加重用户永远不需要的代码,并在初次加载的时候减少所需加载的代码量
# React.lazy
React.lazy
函数能让你像渲染常规组件一样处理动态引入(的组件)
import React ,{ lazy } from 'react';
// React.lazy接受一个函数,这个函数需要动态调用import。
const Lazy = lazy(() => import('./Lazy'));
function App() {
return (
<div>
<Lazy />
<div/>
)
}
# Suspense
在App
组件渲染完成时,Lazy
组件还没有完成加载,则会报错提示,所以可以使用Suspense
这个加载指示器为组件做降级处理
import React ,{ Suspense , lazy } from 'react';
// React.lazy接受一个函数,这个函数需要动态调用import。
const Lazy = lazy(() => import('./Lazy'));
function App() {
return (
<div>
{/* Suspense 接受 一个 React元素 可以是一个组件 也可以是 一个 <div></div> */}
<Suspense fallback={<Loading />} >
<Lazy />
</Suspense>
<div/>
)
}
- Suspense 可以包裹多个懒加载组件
# 基于路由的代码分割
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React , { Suspense , lazy } from 'react';
const Home = lazy(() => import('./pages/home'));
const User = lazy(() => import('./pages/user'));
function Loading() {
return <div>Loading....</div>
}
function App() {
return (
<Router>
<Suspense fallback={ <Loading /> }>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/user' component={User} />
</Switch>
</Suspense>
</Router>
)
}
# 错误边界
a
# Memo
先对普通 class 中 component
中的 shouldComponentUpdate
和 PureComponent
和 React.Memo
作比较:
案例:
import React from 'react';
class Memo extends React.component {
state = {
time: new Date()
}
componentDidMount() {
setInterval(() => {
this.setState({
time: 1
})
},1000)
}
render() {
console.log('render....')
return <div>{this.state.time.toString()}</div>
}
}
在案例中,我们在 componentDidMount
中我们使用setInterval
将 time
更改为 1,但是每次setState之后,都会执行一次render()方法,但是 time
的值都是 1,并没有发生改变,所以我们要进行性能优化,如果值没有发生改变的话,我们不想让render()方法执行;
普通 class 类组件 中的
shouldComponentUpdate
方法shouldComponentUpdate
仅检查了props.color
或state.count
是否改变。如果这些值没有改变,那么这个组件不会更新
import React from 'react'; class Memo extends React.component { state = { time: new Date() } componentDidMount() { setInterval(() => { this.setState({ time: 1 }) },1000) } shouldComponentUpdate(nextProps,nextState) { // nextProps 更新后的Props // nextState 更新后的State return ( this.state.time === nextState.time ? false : true // false 表示不更新 true 表示更新 ) } render() { console.log('render....') return <div>{this.state.time.toString()}</div> } }
使用
PureComponent
- 大部分情况下,你可以使用
React.PureComponent
来代替手写shouldComponentUpdate
。但它只进行浅比较,所以当 props 或者 state 某种程度是可变的话,浅比较会有遗漏,那你就不能使用它了。当数据结构很复杂时,情况会变得麻烦
import React , { PureComponent } from 'react'; class Memo extends PureComponent { state = { time: new Date() } componentDidMount() { setInteval(() => { setState({ time: 1 }) },1000) } render() { console.log('render....') return <div>{this.state.time.toString()}</div> } }
- 大部分情况下,你可以使用
使用React.memo
React.memo
为高阶组件。它与React.PureComponent
非常相似,但它适用于函数组件,但不适用于 class 组件。- 如果你的函数组件在给定相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在
React.memo
中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。
// Memo父组件 import React, { useState, useEffect, PureComponent } from 'react'; import Child from './Child'; class Memo extends React.Component { state = { time: new Date() } componentDidMount() { setInterval(() => { this.setState({ time: new Date() }) }, 1000) } render() { console.log('memo render...') return ( <div> <Child seconds={ 1 } /> <p>{this.state.time.toString()}</p> </div> ) } } export default Memo; // Child组件 import React from 'react'; const Child = ({seconds}) => { console.log('child render....'); return <div>{seconds} seconds.</div> } /** 1、如果我们就这样写的话,当父组件的state发生变化的时候,父组件的render也会重新执行(废话); 2、子组件也会重新执行,但是子组件的值一直都是1,并没有发生变化(需要优化); 3、使用React.memo包裹组件就完事了 */ export defalut React.memo(Child);