React原理

1、 组件的跨层级通信
React中使⽤用Context实现祖代组件向后代组件跨层级传值
(1) React.createContext
创建Context 对象。
(2) Context.Provider
Provider 接收 value 属性,传递给消费组件,允许消费组件订阅 context 的变化。一个 Provider可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使⽤,⾥层的会覆盖外层的数据。
当 Provider 的 value 值发⽣变化时,它内部的所有消费组件都会重新渲染。
provider变化会引起多个消费组件进行不必要的渲染,浪费性能,要避免。
示例

class App extends React.Component {
    render() {
        return (
            
                
            
        );
    }
}

将provider的value状态提升到父组件的state里面

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            value: {something: 'something'},
        };
    }
    render() {
        return (
            
                
            
        );
    }
}

(3) Class.contextType
挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context
对象,可以在任何生命周期中访问到它,包括 render 函数中。用法存在局限性。
static contextType = ThemeContext;
[ClassName].contextType = ThemeContext;
(4) Context.Consumer
用于Class型组件订阅 context 变更。
(5) useContext
用于Function型组件订阅 context 变更。接收⼀个 context 对象( React.createContext 的返回值)并返回该 context 的当前值。当前的context 值由上层组件中距离当前组件最近的 的 value prop 决定。

// context.js 创建 
import React from "react";
// 定义两个context传两个值 
// provider和comsumer从context里获取
export const ThemeContext = React.createContext({themeColor: "pink"});
export const UserContext = React.createContext();
import React, {Component} from "react";
import ContextTypePage from "./ContextTypePage";
import {ThemeContext, UserContext} from "../Context";
import UseContextPage from "./UseContextPage";
import ConsumerPage from "./ConsumerPage";
export default class ContextPage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            theme: {
                themeColor: "red"
            },
            user: {
                name: "xiaoming"
            }
        };
    }
    changeColor = () => {
        const {themeColor} = this.state.theme;
        this.setState({
            theme: {
                 themeColor: themeColor === "red" ? "green" : "red"
            }
        });
};

    render() {
        const {theme, user} = this.state;
        return (
              

ContextPage

。// context的嵌套 // contextType方法获取 // 可以消费多个context
); } }

pages/ContextTypePage.js

import React, {Component} from "react";
import {ThemeContext} from "../Context";

export default class ContextTypePage extends Component {
    static contextType = ThemeContext; // 静态方法挂载在class下
    render() {
        const {themeColor} = this.context;
        return (
            

ContextTypePage

); } }

pages/ConsumerPage.js

import React, {Component} from "react";
import {ThemeContext, UserContext} from "../Context";
// class组件中使用comsumer的方法
export default class ConsumerPage extends Component {
        render() {
            return (
               
// 接收context {themeContext => ( <>

ConsumerPage

{userContext => } // 提取出组件 )}
); } } // 防止嵌套太多可以将组件提出来, 接受context参数 function HandleUserContext(userCtx) { return
{userCtx.name}
; }

pages/UseContextPage

import React, {useState, useEffect, useContext} from "react";
import {ThemeContext, UserContext} from "../Context";
// function组件使用context方法
export default function UseContextPage(props) {
    const themeContext = useContext(ThemeContext); // 使用useContext钩子接受context
    const {themeColor} = themeContext; // es6 方法提取出来
    const userContext = useContext(UserContext); 
        return (
            

UseContextPage

// 在组件中直接使用

{userContext.name}

// 直接使用
); }

2、高阶组件HOC
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
高阶组件是参数为组件,返回值为新组件的函数。
组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。

import React, {Component} from "react";
// hoc: 是⼀一个函数,接收⼀一个组件,返回另外⼀一个组件
//这⾥大写开头的Cmp是指function或者class组件
// 定义高阶组件
const foo = Cmp => props => {
    return (
        
); }; // 定义传入高阶组件的组件 function Child(props) { return
Child {props.name}
; } // 组装高阶组件 const Foo = foo(Child); // 赋值和使用 export default class HocPage extends Component { render() { return (

HocPage

); } }

装饰器写法
利用ES7中出现的装饰器语法,首先安装和配置。
yarn add @babel/plugin-proposal-decorators

更新config-overrides.js

//配置完成后记得重启下
const { addDecoratorsLegacy } = require("customize-cra");
module.exports = override(
    ...,
    addDecoratorsLegacy()//配置装饰器器
);

如果vscode对装饰器有warning,vscode设置里加上
javascript.implicitProjectConfig.experimentalDecorators": true

//HocPage.js
// !装饰器器只能⽤用在class上
// 执⾏行行顺序从下往上
@foo
@foo
class Child extends Component {
    render() {
        return 
Child {this.props.name}
; } } // const Foo = foo2(foo(foo(Child))); export default class HocPage extends Component { render() { return (

HocPage

{/* */}
); } }

注意事项:
不要在 render 方法中使用 HOC

render() {
// 每次调⽤用 render 函数都会创建⼀一个新的 EnhancedComponent
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// 这将导致⼦子树每次渲染都会进⾏行行卸载,和重新挂载的操作!
return ;
}
  1. Redux原理
    (1)reducer
    reducer 就是⼀个纯函数,接收旧的 state 和 action,返回新的 state
;(previousState, action) => newState

称之为 reducer,是因为这种函数与被传⼊ Array.prototype.reduce(reducer, ?initialValue) ⾥的回调函数属于相同的类型

(2) reduce和聚合函数

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator
+ currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15

将以下函数生成聚合函数,把第一个函数的返回值传给下一个函数。

function f1(arg) {
console.log("f1", arg);
return arg;
}
function f2(arg) {
console.log("f2", arg);
return arg;
}
function f3(arg) {
console.log("f3", arg);
return arg;
}
// 普通调用
const  res = f1(f2(f3('omg')));
// output: f3 omg  f2 omg  f1 omg

// 使用聚合函数
function compose(...funcs) {
    if (funcs.length === 0) {
        return arg => arg
    }
    if (funcs.length === 1) {
        return funcs[0]
    }
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
console.log(compose(f1, f2, f3)("omg"));

(3) redux流程

  1. 需要⼀个store来存储数据
  2. store⾥的reducer初始化state并定义state修改规则
  3. 通过dispatch⼀个action来提交对数据的修改
  4. action提交到reducer函数⾥,根据传⼊的action的type,返回新的state

store存储数据,src/store/index.js

import {createStore} from "redux";
// 初始化 修改状态函数
function countReducer(state = 0, action) {
    switch (action.type) {
        case "ADD":
            return state + 1;
        case "MINUS":
            return state - 1;
        default:
            return state;
    }
}
const store = createStore(countReducer); // 创建store
export default store;

创建ReduxPage

import React, {Component} from "react";
import store from "../store/";
export default class ReduxPage extends Component {
    componentDidMount() {
        store.subscribe(() => { // 变更订阅
            this.forceUpdate();
        });
    }
    add = () => {
        store.dispatch({type: "ADD"}); // dispatch 更改数据,reducer里已经定义好了更改规则
    };
    minus = () => {
        store.dispatch({type: "MINUS"});
    };
    render() {
        return (
          

ReduxPage

{store.getState()}

// getState获取数据
); } }

(3) Redux拓展
实现:
存储状态state
获取状态getState
更新状态dispatch
变更订阅subscribe

// 实现createStore,返回getState,dispatch,subscribe函数
export default function createStore(reducer, enhancer) {
    // ehancer为applyMiddleware返回的结果
    if (enhancer) {
        return enhancer(createStore)(reducer);
    }
    // 函数内存储state的值
    let currentState;
    // 函数内存储监听数组
    let currentListeners = [];
    function getState() {
        return currentState; // 返回当前的值
    }
    function dispatch(action) {
        currentState = reducer(currentState, action); // 用reducer规则更新currentState
        currentListeners.forEach(listener => listener()); 
        //执行每一个listener,告诉subscribe的地方更新currentState(指的是页面重新渲染,展示新的值)
        return action;
    }
    function subscribe(listener) {
        currentListeners.push(listener); // 传入一个listener,加入到监听数组
        return () => {
            currentListeners = []; // 返回一个销毁数组的方法
        };
    }
    dispatch({type: "KKBREDUX/OOOO"});  // 执行reducer中的default
    return {
        getState,
        dispatch,
        subscribe
    };
}

(5) Redux中间件
Redux只是个纯粹的状态管理器,默认只⽀持同步,实现异步任务 ⽐如延
迟,⽹络请求,需要中间件的⽀持,⽐如我们使⽤最简单的redux-thunk和
redux-logger 。


image.png

应用中间价 store.js

import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";
import counterReducer from './counterReducer'
const store = createStore(counterReducer,applyMiddleware(thunk, logger));
    asyAdd = () => {
        // 实现一个中间件使dispatch能够接收函数
        store.dispatch((dispatch, getState) => {
        setTimeout(() => {
        // console.log("now ", getState()); //sy-log
        dispatch({type: "ADD", payload: 1});
        }, 1000);
    });
};

实现 applyMiddleware

export default function applyMiddleware(...middlewares) {
    return createStore => reducer => {
        // 从 createStore(reducer)里拿到dispatch,getState
        const store = createStore(reducer);
        let dispatch = store.dispatch;
       // 柯里化 先传入参数,闭包保存
        const midApi = {
            getState: store.getState,
            dispatch: (action, ...args) => dispatch(action,
            ...args)
        };
    // 先传入参数执行
    const middlewareChain = middlewares.map(middleware =>
    middleware(midApi));
    // 聚合函数加强dispatch
    dispatch = compose(...middlewareChain)(store.dispatch);
    return {
        ...store,
        // 加强版的dispatch
        dispatch
        };
    };
}
function compose(...funcs) {
    if (funcs.length === 0) {
        return arg => arg;
    }
    if (funcs.length === 1) {
        return funcs[0];
    }
    return funcs.reduce((a, b) => (...args) =>
    a(b(...args)));
}

实现redux-logger

function logger({getState}) {
    // action 在调用加强后dispach时传进来的 dispatch({type:'ADD'})
    // next 表示聚合函数 f1( f2(...args) ) 中的 返回值,会被外层调用
    return next => action => {
        console.log("====================================");
        console.log(action.type + "执⾏了!"); //sy-log
        const prevState = getState();
        console.log("prev state", prevState); //sy-log
        const returnValue = next(action);
        const nextState = getState();
        console.log("next state", nextState); //sy-log
        console.log("====================================");
        return returnValue;
    };
}

实现 redux-thunk

function thunk({dispatch, getState}) {
    return next => action => {
        // 增加了处理函数的能力
        if (typeof action === "function") {
            return action(dispatch, getState);
        }
        return next(action);
    };
}

实现redux-promis

function promise({dispatch}) {
    return next => action => {
        return isPromise(action) ? action.then(dispatch) :
        next(action);
    };
}
  1. react-redux
    (1) react-hooks

useReducer
useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      
      
    
  );
}

useCallback

// 返回一个依赖于参数a,b执行的回调函数 memoizedCallback
const memoizedCallback = useCallback(
    () => {
      doSomething(a, b);
    },
    [a, b],
);

useMemo
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useLayoutEffect
其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

(2) 使用react-redux
在redux的基础上添加了两个api

  1. Provider 为后代组件提供store , 替代getState方法获取数据,使用 useContext
  2. connect 为组件提供数据和变更方法,替代subscribe方法,执行绑定数据和更新组件功能

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

参数

  • mapStateToProps(state, [ownProps]): stateProps ] (Function)
    该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果定义该参数,组件将会监听 Redux store 的变化,否则 不监听。
    ownProps 是当前组件自身的props,如果指定了,那么只要组件接收到新的 props,mapStateToProps 就会被调用,mapStateToProps 都会被重新计算, mapDispatchToProps 也
    会被调用。注意性能!
  • mapDispatchToProps(dispatch, [ownProps]): dispatchProps ] (Object or Function):
    如果省略这个 mapDispatchToProps 参数,dispatch 会注入到你的组件 props
    中。可传递对象和函数,见示例。

全剧提供store index.js

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
// import {Provider} from "react-redux";
import {Provider} from "./kReactRedux";
import store from "./store/";
// 把Provider放在根组件外层,使子组件能获得store
ReactDOM.render(
    
        
    ,
    document.getElementById("root")
);
import React, {Component} from "react";
import {connect} from "react-redux";
import {bindActionCreators} from "redux";
// import {bindActionCreators, connect} from "../kReactRedux";
// connect用于连接React组件与store, 返回一个新的已经与store连接的组件类(HOC)

export default connect(
    // 第一个参数 mapStateToProps, state 从 store中获取值
    (state, ownProps) => {
        return {
            count: state.count
        };
    },
    // 第二个参数mapDispatchToProps,可以是 Object / Fucntion
    // Object形式
   {
     add: () => ({type: "ADD"}),
     minus: () => ({type: "MINUS"})
   }

   // Fucntion形式,参数是dispatch与ownProps
   // 使用 bindActionCreators 函数, 作用是绑定dispatch和creators
  (dispatch, ownProps) => {
      let creators = {
        add: payload => ({type: "ADD", payload}),
        minus: () => ({type: "MINUS"})
      };
    creators = bindActionCreators(creators, dispatch);
    return {dispatch, ...creators};
  }
   // 不使用
  (dispatch, ownProps) => {
      return {
          add: payload => dispatch({type: "ADD", payload}),
          minus: () => dispatch({type: "MINUS"})
    }
  };
  }
)(

class ReactReduxPage extends Component {
    add = () => {
      this.props.dispatch({type: "ADD"});
    };
    render() {
      const {count, dispatch, add, minus} = this.props;
      return (
        

ReactReduxPage

omg:{count}

); } } );

实现react-redux

import React, {useContext, useReducer, useLayoutEffect} from "react";
const Context = React.createContext();
export const connect = (
    mapStateToProps = state => state,
    mapDispatchToProps
) => WrappendComponent => props => {
    // 定义全局context存储store
    const store = useContext(Context);
    const {dispatch, getState, subscribe} = store;
    // 得到stateProps对象, 直接传入组件
    const stateProps = mapStateToProps(getState());
    let dispatchProps = {dispatch};
    // hooks实现forceupdate
    const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
    
    if (typeof mapDispatchToProps === "function") {
        dispatchProps = mapDispatchToProps(dispatch);
    } else if (typeof mapDispatchToProps === "object") {
        dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
    }
    useLayoutEffect(() => {
    const unsubscribe = subscribe(() => {
        forceUpdate();
    });
    return () => {
        if (unsubscribe) {
            unsubscribe();
        }
    };
  }, [store]);
  return ;
};

export function Provider({store, children}) {
    return {children};
}

function bindActionCreator(creator, dispatch) {
    return (...args) => dispatch(creator(...args));
}

function bindActionCreators(creators, dispatch) {
    const obj = {};
    for (let key in creators) {
        obj[key] = bindActionCreator(creators[key], dispatch);
    }
    return obj;
}

(3) react-redux hooks API及实现
useSelector 获取store state
useDispatch 获取dispatch

import React, {useCallback} from "react";
import {useSelector, useDispatch} from "react-redux";
export default function ReactReduxHookPage({value}) {
    const dispatch = useDispatch();
    const add = useCallback(() => {
        dispatch({type: "ADD"});
    }, []);
    const count = useSelector(({count}) => count);
    return (
        

ReactReduxHookPage

{count}

); }

实现

export function useSelector(selector) {
    const store = useStore();
    const {getState, subscribe} = store;
    const selectedState = selector(getState());
    const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
    useLayoutEffect(() => {
        const unsubscribe = subscribe(() => {
        forceUpdate();
        });
        return () => {
           if (unsubscribe) {
              unsubscribe();
           }
        };
    }, [store]);
    return selectedState;
}

export function useDispatch() {
    const store = useStore();
    return store.dispatch;
}

export function useStore() {
    const store = useContext(Context);
    return store;
}
  1. React-router

你可能感兴趣的:(React原理)