# 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值

# 注意:

  1. 消费多个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>
      );
    }
    
  2. 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中的 shouldComponentUpdatePureComponentReact.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 中我们使用setIntervaltime 更改为 1,但是每次setState之后,都会执行一次render()方法,但是 time 的值都是 1,并没有发生改变,所以我们要进行性能优化,如果值没有发生改变的话,我们不想让render()方法执行;

  • 普通 class 类组件 中的 shouldComponentUpdate 方法

    • shouldComponentUpdate 仅检查了 props.colorstate.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);
    

    官网文档说明