React高级部分(二)

React UI组件库

概述

material-ui(国外)

官网:https://mui.com/zh/#/

ant-design(国内蚂蚁金服)

官网:https://ant.design/index-cn

antdesign的使用

安装Node.js

备注:安装完Node.js,默认会安装node和npm。

安装yarn包管理器

命令:

npm -g install yarn

npm、yarn设置源

借助cgr,cgr是一款能同时、分开管理npm、yarn源的工具。

安装cgr:

npm -g install cgr

查看默认源列表:

cgr ls
React高级部分(二)

备注:

Y代表yarn。

N代表npm。

*代表npm和yarn共同的源。

切换源:

cgr use 源名称 [类型,y/yarn表示yarn切换;n/npm表示npm切换;为空,表示同时切换源]
React高级部分(二)

添加源:

cgr add 源名称 源地址
React高级部分(二)

删除源:

cgr del 源名称
React高级部分(二)

源响应时间测试:

cgr test
React高级部分(二)

安装React脚手架和初始化React项目

全局安装:

npm -g install create-react-app

切换到你想创建项目的目录:

create-react-app antd

进入项目文件夹:

cd antd

启动项目:

yarn run start

准备工作

目录结构:

React高级部分(二)

引入antd

命令:

yarn add antd@4.16.0

APP.jsx

import React, {Component} from 'react';
import {Button} from 'antd';
import {WechatOutlined,SearchOutlined } from '@ant-design/icons';
import 'antd/dist/antd.css'

export default class App extends Component {

    render() {
        return (
            <div>
                <br/>
                <Button type="primary">Primary Button</Button>
                <Button>Default Button</Button>
                <Button type="dashed">Dashed Button</Button>
                <Button type="text">Text Button</Button>
                <Button type="link">Link Button</Button>
                <Button type="primary" shape="circle" icon={<SearchOutlined />} ></Button>
                <WechatOutlined />
            </div>

        );
    }
}

样式按需加载

安装craco:

yarn add @craco/craco

修改package.json

/* package.json */
"scripts": {
-   "start""react-scripts start",
-   "build""react-scripts build",
-   "test""react-scripts test",
+   "start""craco start",
+   "build""craco build",
+   "test""craco test",
}

安装babel-plugin-import:

yarn add babel-plugin-import -D

在项目根目录创建一个 craco.config.js ,用于修改默认配置:

module.exports = {
  babel: {
    plugins: [
      ['import', { libraryName'antd'styletrue }],
    ]
  },
};

APP.jsx(删除import 'antd/dist/antd.css'):

import React, {Component} from 'react';
import {Button} from 'antd';
import {WechatOutlined,SearchOutlined } from '@ant-design/icons';

export default class App extends Component {

    render() {
        return (
            <div>
                <br/>
                <Button type="primary">Primary Button</Button>
                <Button>Default Button</Button>
                <Button type="dashed">Dashed Button</Button>
                <Button type="text">Text Button</Button>
                <Button type="link">Link Button</Button>
                <Button type="primary" shape="circle" icon={<SearchOutlined />} ></Button>
                <WechatOutlined />
            </div>

        );
    }
}

修改默认主题

安装craco-less:

yarn add craco-less

修改craco.config.js,内容如下:

const CracoLessPlugin = require('craco-less');

module.exports = {
    plugins: [
        {
            plugin: CracoLessPlugin,
            options: {
                lessLoaderOptions: {
                    lessOptions: {
                        modifyVars: { "@primary-color""#1DA57A" },
                        javascriptEnabledtrue,
                    },
                },
            },
        },
    ],
    babel: {
        plugins: [
            ['import', { libraryName'antd'styletrue }],
        ]
    },
};

Redux

Redux的理解

文档

英文文档:https://redux.js.org/

中文文档:https://www.redux.org.cn/

GitHub:https://github.com/reactjs/redux

Redux是什么?

  • redux是一个专门用来做状态管理的JS库(不是react插件库)。
  • 它可以用在React、Angular、Vue等项目中,但是基本上和React配合使用。
  • 作用:集中式管理React应用中多个组件共享的状态。

什么情况下使用Redux?

  • 某个组件的状态,需要让其他组件可以随时拿到(共享)。
  • 一个组件需要改变另一个组件的状态(通信)。
  • 总体原则:能不用就不用,如果不用比较吃力才考虑使用。

Redux的工作流程

React高级部分(二)

Redux的三个核心概念

action

动作的对象。

包含2个属性:

  • type:标识属性,值为字符串,唯一,必要属性。
  • data:数据属性,值类型任意,可选属性。

例子:{type:'ADD_STUDENT',data:{name:'tom',age:18}}

reducer

用于初始化状态、加工状态。

加工的时候,根据旧的state和action,产生新的state的纯函数

store

将state、action、reducer联系在一起的对象。

如何得到此对象?

import {createStore} from 'redux'
import reducer from './reducers'
const store = createStore(reducer)

此对象的功能?

// 得到state
getState()
// 分发action,触发reducer调用,产生新的state。
dispatch(action)
// 注册监听,当产生了新的state时,自动调用。
subscribe(listener)

redux的核心API

createStore(reducer)

作用:创建包含reducer的store对象。

store对象

作用:redux库最核心的管理对象。

它内部维护着:

  • state。
  • reducer。

核心方法:

  • getState()。
  • dispatch(action)。
  • subscribe(listener)。

applyMiddleware()

作用:应用上基于redux的中间件(插件库)。

combineReducers()

作用:合并多个reducer函数。

使用redux编写应用

纯React版求和案例

准备工作

目录结构:

React高级部分(二)

应用示例

示例:

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/APP.jsx

import React, {Component} from 'react';
import Count from './component/Count'

export default class App extends Component {
  render() {
    return (
       <Count/>
    );
  }
}

src/components/Count/index.jsx

import React, {Component} from 'react';

export default class Count extends Component {

    state = {
        count0
    }

    //加法
    increment = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //从状态中获取值
        const {count} = this.state;
        //更新状态
        this.setState({count: count + parseInt(value)});
    }

    //减法
    decrement = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //从状态中获取值
        const {count} = this.state;
        //更新状态
        this.setState({count: count - parseInt(value)});
    }

    //当前求和为奇数才加
    incrementIfOdd = () => {
        //从状态中获取值
        const {count} = this.state;
        //判断是否为奇数
        if (count % 2 !== 0) {
            //获取选中的值
            const {value} = this.selectNumber;
            //更新状态
            this.setState({count: count + parseInt(value)});
        }
    }

    //异步加
    incrementAsync = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //从状态中获取值
        const {count} = this.state;
        //异步加
        setTimeout(() => {
            this.setState({count: count + parseInt(value)});
        }, 500);
    }

    render() {
        const {count} = this.state;
        return (
            <div>
                <h2>当前求和为:{count}</h2>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;&nbsp;
                <button onClick={this.increment}>+</button>
                &nbsp;&nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>

        );
    }
}

redux精简版求和案例

准备工作

目录结构:

React高级部分(二)

安装redux:

yarn add redux

应用示例

示例:

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/APP.jsx

import React, {Component} from 'react';
import Count from './component/Count'
import store from './redux/store'

export default class App extends Component {

    componentDidMount() {
        //注册监听,当产生了新的state的时候,自动调用
        store.subscribe(() => {
            this.setState({});
        });
    }

    render() {
        return (
            <Count/>
        );
    }
}

src/redux/store.js

/**
 * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
 */

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//创建并暴露store对象
export default createStore(countReducer);

src/redux/count_reducer.js

/**
 * 该文件是用于为Count组件服务的Reducer,Reducer的本质就是一个函数。
 * reducer函数会接收到两个参数,分别为:prevState(之前的对象)和action(动作对象)
 *
 * @param prevState 先前的状态,默认值为0
 * @param action 动作对象
 */

export default function countReducer(prevState = 0, action{
    //从action对象中获取type和data
    const {type, data} = action;
    //根据type决定如何加工数据
    switch (type) {
        //加法
        case "increment":
            return prevState + data;
        //减法
        case "decrement":
            return prevState - data;
        //默认情况:没有匹配到,就是默认情况,就是reducer进行初始化
        default:
            return prevState;
    }
}

src/component/Count/index.jsx

import React, {Component} from 'react';
//引入store,用于获取redux中保存的state
import store from '../../redux/store.js'

export default class Count extends Component {

    // componentDidMount() {
    //     //注册监听,当产生了新的state的时候,自动调用
    //     store.subscribe(() => {
    //         this.setState({});
    //     });
    // }

    //加法
    increment = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //分发action,触发reducer调用,产生新的state
        store.dispatch({
            type'increment',
            dataparseInt(value)
        })
    }

    //减法
    decrement = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //分发action,触发reducer调用,产生新的state
        store.dispatch({
            type'decrement',
            dataparseInt(value)
        })
    }

    //当前求和为奇数才加
    incrementIfOdd = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //获取redux中维护的state
        let count = store.getState();
        if (count % 2 !== 0) {
            store.dispatch({
                type'increment',
                dataparseInt(value)
            })
        }
    }

    //异步加
    incrementAsync = () => {
        setTimeout(() => {
            //获取选中的值
            const {value} = this.selectNumber;
            //分发action,触发reducer调用,产生新的state
            store.dispatch({
                type'increment',
                dataparseInt(value)
            })
        }, 500);
    }

    render() {
        return (
            <div>
                <h2>当前求和为:{store.getState()}</h2>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;&nbsp;
                <button onClick={this.increment}>+</button>
                &nbsp;&nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>

        );
    }
}

redux完整版求和案例

准备工作

目录结构:

React高级部分(二)

应用示例

示例:

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/APP.jsx

import React, {Component} from 'react';
import Count from './component/Count'
import store from './redux/store'

export default class App extends Component {

    componentDidMount() {
        //注册监听,当产生了新的state的时候,自动调用
        store.subscribe(() => {
            this.setState({});
        });
    }

    render() {
        return (
            <Count/>
        );
    }
}

src/redux/constant.js

/**
 * 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时,防止程序员单词写错
 */

export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';

src/redux/count_action.js

/**
 * 该文件专门为Count组件生成action对象。
 */

import {INCREMENT, DECREMENT} from './constant'

export const createIncrementAction = data => ({type: INCREMENT, data: data})

export const createDecrementAction = data => ({type: DECREMENT, data: data})

src/redux/count_reducer.js

import {INCREMENT, DECREMENT} from './constant'

/**
 * 该文件是用于为Count组件服务的Reducer,Reducer的本质就是一个函数。
 * reducer函数会接收到两个参数,分别为:prevState(之前的对象)和action(动作对象)
 *
 * @param prevState 先前的状态,默认值为0
 * @param action 动作对象
 */

export default function countReducer(prevState = 0, action{
    //从action对象中获取type和data
    const {type, data} = action;
    //根据type决定如何加工数据
    switch (type) {
        //加法
        case INCREMENT:
            return prevState + data;
        //减法
        case DECREMENT:
            return prevState - data;
        //默认情况:没有匹配到,就是默认情况,就是reducer进行初始化
        default:
            return prevState;
    }
}

src/redux/store.js

/**
 * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
 */

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//创建并暴露store对象
export default createStore(countReducer);

src/component/Count/index.jsx

import React, {Component} from 'react';
//引入store,用于获取redux中保存的state
import store from '../../redux/store.js'
//引入actionCreator,专门用于创建action对象
import {createIncrementAction, createDecrementAction} from '../../redux/count_action'

export default class Count extends Component {

    //加法
    increment = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //分发action,触发reducer调用,产生新的state
        store.dispatch(createIncrementAction(parseInt(value)));
    }

    //减法
    decrement = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //分发action,触发reducer调用,产生新的state
        store.dispatch(createDecrementAction(parseInt(value)))
    }

    //当前求和为奇数才加
    incrementIfOdd = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //获取redux中维护的state
        let count = store.getState();
        if (count % 2 !== 0) {
            store.dispatch(createIncrementAction(parseInt(value)));
        }
    }

    //异步加
    incrementAsync = () => {
        setTimeout(() => {
            //获取选中的值
            const {value} = this.selectNumber;
            //分发action,触发reducer调用,产生新的state
            store.dispatch(createIncrementAction(parseInt(value)));
        }, 500);
    }

    render() {
        return (
            <div>
                <h2>当前求和为:{store.getState()}</h2>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;&nbsp;
                <button onClick={this.increment}>+</button>
                &nbsp;&nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>

        );
    }
}

redux的异步编程

概述

redux默认是不能进行异步处理的。

某些时候应用中需要在redux中执行异步任务(ajax、定时器)。

需要借助中间件redux-thunk:

yarn add redux-thunk

准备工作

目录结构:

React高级部分(二)

应用示例

示例:

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/APP.jsx

import React, {Component} from 'react';
import Count from './component/Count'
import store from './redux/store'

export default class App extends Component {

    componentDidMount() {
        //注册监听,当产生了新的state的时候,自动调用
        store.subscribe(() => {
            this.setState({});
        });
    }

    render() {
        return (
            <Count/>
        );
    }
}

src/redux/constant.js

/**
 * 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时,防止程序员单词写错
 */

export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';

src/redux/store.js

/**
 * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
 */

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//创建并暴露store对象
export default createStore(countReducer,applyMiddleware(thunk));

src/redux/count_Reducer.js

import {INCREMENT, DECREMENT} from './constant'

/**
 * 该文件是用于为Count组件服务的Reducer,Reducer的本质就是一个函数。
 * reducer函数会接收到两个参数,分别为:prevState(之前的对象)和action(动作对象)
 *
 * @param prevState 先前的状态,默认值为0
 * @param action 动作对象
 */

export default function countReducer(prevState = 0, action{
    //从action对象中获取type和data
    const {type, data} = action;
    //根据type决定如何加工数据
    switch (type) {
        //加法
        case INCREMENT:
            return prevState + data;
        //减法
        case DECREMENT:
            return prevState - data;
        //默认情况:没有匹配到,就是默认情况,就是reducer进行初始化
        default:
            return prevState;
    }
}

src/redux/count_action.js

/**
 * 该文件专门为Count组件生成action对象。
 */

import {INCREMENT, DECREMENT} from './constant'

//同步action,就是指action的值为object类型的一般对象
export const createIncrementAction = data => ({type: INCREMENT, data: data})

export const createDecrementAction = data => ({type: DECREMENT, data: data})

//异步action,就是指action的值为函数,异步action一般都会调用同步action
export const createIncrementAsyncAction = (data, time) => {
    return (dispatch) => {
        setTimeout(() => {
            dispatch(createIncrementAction(data))
        }, time);
    };
}

src/component/Count/index.jsx

import React, {Component} from 'react';
//引入store,用于获取redux中保存的state
import store from '../../redux/store.js'
//引入actionCreator,专门用于创建action对象
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from '../../redux/count_action'

export default class Count extends Component {

    //加法
    increment = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //分发action,触发reducer调用,产生新的state
        store.dispatch(createIncrementAction(parseInt(value)));
    }

    //减法
    decrement = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //分发action,触发reducer调用,产生新的state
        store.dispatch(createDecrementAction(parseInt(value)))
    }

    //当前求和为奇数才加
    incrementIfOdd = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //获取redux中维护的state
        let count = store.getState();
        if (count % 2 !== 0) {
            store.dispatch(createIncrementAction(parseInt(value)));
        }
    }

    //异步加
    incrementAsync = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        //分发action,触发reducer调用,产生新的state
        store.dispatch(createIncrementAsyncAction(parseInt(value)));
    }

    render() {
        return (
            <div>
                <h2>当前求和为:{store.getState()}</h2>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;&nbsp;
                <button onClick={this.increment}>+</button>
                &nbsp;&nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>

        );
    }
}

react-redux

概述

一个react插件库。

专门用来简化React应用中使用redux。

安装react-redux:

yarn add react-redux

react-redux的工作原理

① 所有的UI组件都应该包裹一个容器组件,他们是父子组件。

② 容器组件是真正和redux打交道的,里面可以随意的使用redux的API。

③ UI组件中不能使用任何redux的API。

④ 容器组件会传递给UI组件:

  • redux中所保存的状态。
  • 用于操作状态的方法。

⑤ 容器给UI传递状态、操作状态(redux中的状态)的方法,均可通过props传递。

React高级部分(二)

react-redux将所有组件分为两大类

UI组件

① 只负责UI的呈现,不带有任何业务逻辑。

② 通过props接收数据(一般数据和函数)。

③ 不使用任何Redux的API。

④ 一般保存在components文件夹下。

容器组件

① 负责管理数据和业务逻辑,不负责UI的呈现。

② 使用Redux的API。

③ 一般保存在containers文件夹下。

相关API

Provider

Provider:让所有组件都可以得到state数据。

<Provider store={store}>
 <APP/>
</Provider>

connect

connect:用于包装UI组件生成容器组件。

import {connect} from 'react-redux'
connect(mapStateToProps, mapDispatchToProps)(CountUI)

mapStateToProps

mapStateToProps:将外部的数据(即state对象)转换为UI组件的标签属性。

const mapStateToProps = (state) => {
    return {count: state};
}

mapDispatchToProps

mapDispatchToProps:将分发的action的函数转换为UI组件的标签属性。

const mapDispatchToProps = (dispatch) => {
    return {
        increment(data) => {
            //通知redux执行加法
            dispatch(createIncrementAction(data));
        },
        decrement(data) => {
            //通知redux执行减法
            dispatch(createDecrementAction(data));
        },
        incrementAsync(data, time) => {
            dispatch(createIncrementAsyncAction(data, time));
        }
    }
}

应用示例

react-redux的基本使用

准备工作:

React高级部分(二)

示例:

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/APP.jsx

import React, {Component} from 'react';
import Count from './containers/Count'
import store from './redux/store'

export default class App extends Component {

    componentDidMount() {
        //注册监听,当产生了新的state的时候,自动调用
        store.subscribe(() => {
            this.setState({});
        });
    }

    render() {
        return (
            /* 给容器组件传递store */
            <Count store={store}/>
        );
    }
}

src/redux/store.js

/**
 * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
 */

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//创建并暴露store对象
export default createStore(countReducer,applyMiddleware(thunk));

src/redux/constant.js

/**
 * 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时,防止程序员单词写错
 */

export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';

src/redux/count_action.js

/**
 * 该文件专门为Count组件生成action对象。
 */

import {INCREMENT, DECREMENT} from './constant'

//同步action,就是指action的值为object类型的一般对象
export const createIncrementAction = data => ({type: INCREMENT, data: data})

export const createDecrementAction = data => ({type: DECREMENT, data: data})

//异步action,就是指action的值为函数,异步action一般都会调用同步action
export const createIncrementAsyncAction = (data, time) => {
    return (dispatch) => {
        setTimeout(() => {
            dispatch(createIncrementAction(data))
        }, time);
    };
}

src/redux/count_reducer.js

import {INCREMENT, DECREMENT} from './constant'

/**
 * 该文件是用于为Count组件服务的Reducer,Reducer的本质就是一个函数。
 * reducer函数会接收到两个参数,分别为:prevState(之前的对象)和action(动作对象)
 *
 * @param prevState 先前的状态,默认值为0
 * @param action 动作对象
 */

export default function countReducer(prevState = 0, action{
    //从action对象中获取type和data
    const {type, data} = action;
    //根据type决定如何加工数据
    switch (type) {
        //加法
        case INCREMENT:
            return prevState + data;
        //减法
        case DECREMENT:
            return prevState - data;
        //默认情况:没有匹配到,就是默认情况,就是reducer进行初始化
        default:
            return prevState;
    }
}

src/containers/Count/index.jsx

//引入Count的UI组件
import CountUI from '../../components/Count'
//引入connect用于连接UI组件和redux
import {connect} from 'react-redux'

import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from '../../redux/count_action'

//mapStateToProps函数返回的是一个对象
//返回的中的key就作为传递给UI组件props的key,value就作为传递给props的value。
//mapStateToProps用于传递状态
const mapStateToProps = (state) => {
    return {count: state};
}

//mapDispatchToProps函数返回的是一个对象
//返回的对象中的key就作为传递给UI组件props的key,value就作为传递给props的value。
//mapDispatchToProps用于传递操作状态的方法。
const mapDispatchToProps = (dispatch) => {
    return {
        increment(data) => {
            //通知redux执行加法
            dispatch(createIncrementAction(data));
        },
        decrement(data) => {
            //通知redux执行减法
            dispatch(createDecrementAction(data));
        },
        incrementAsync(data, time) => {
            dispatch(createIncrementAsyncAction(data, time));
        }
    }
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);

src/components/Count/index.jsx

import React, {Component} from 'react';

export default class Count extends Component {

    //加法
    increment = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        this.props.increment(parseInt(value));
    }

    //减法
    decrement = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        this.props.decrement(parseInt(value));
    }

    //当前求和为奇数才加
    incrementIfOdd = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        const {count} = this.props;
        if (count % 2 !== 0) {
            this.props.increment(parseInt(value));
        }
    }

    //异步加
    incrementAsync = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        this.props.incrementAsync(parseInt(value), 500);
    }

    render() {
        const {count} = this.props;
        return (
            <div>
                <h2>当前求和为:{count}</h2>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;&nbsp;
                <button onClick={this.increment}>+</button>
                &nbsp;&nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>

        );
    }
}

mapDispatchToProps的优化

准备工作:

React高级部分(二)

示例:

src/container/Count/index.jsx

//引入Count的UI组件
import CountUI from '../../components/Count'
//引入connect用于连接UI组件和redux
import {connect} from 'react-redux'
//引入action
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from '../../redux/count_action'
//使用connect()()创建并暴露一个Count的容器组件
export default connect(
    state => ({count: state}),
    /*
        mapDispatchToProps的一般写法
        dispatch => ({
            increment: (data) => {
                //通知redux执行加法
                dispatch(createIncrementAction(data));
            },
            decrement: (data) => {
                //通知redux执行减法
                dispatch(createDecrementAction(data));
            },
            incrementAsync: (data, time) => {
                dispatch(createIncrementAsyncAction(data, time));
            }
        })
    */

    /* mapDispatchToProps的精简写法 */
    {
        increment: createIncrementAction,
        decrement: createDecrementAction,
        incrementAsync: createIncrementAsyncAction
    }
)(CountUI);

不需要手动检测redux的状态变化

使用了react-redux之后,不需要手动检测redux的状态变化,那么将APP.jsx中的检测redux变化的钩子注释掉。

src/APP.jsx

import React, {Component} from 'react';
import Count from './containers/Count'
import store from './redux/store'

export default class App extends Component {

    // componentDidMount() {
    //     //注册监听,当产生了新的state的时候,自动调用
    //     store.subscribe(() => {
    //         this.setState({});
    //     });
    // }

    render() {
        return (
            /* 给容器组件传递store */
            <Count store={store}/>
        );
    }
}

Provider组件的使用

Provider组件让所有组件都可以得到state数据。

准备工作:

React高级部分(二)

示例:

src/APP.jsx(Count组件不需要单独传递store)

import React, {Component} from 'react';
import Count from './containers/Count'

export default class App extends Component {

    // componentDidMount() {
    //     //注册监听,当产生了新的state的时候,自动调用
    //     store.subscribe(() => {
    //         this.setState({});
    //     });
    // }

    render() {
        return (
            <Count/>
        );
    }
}

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux'
import App from './App';
import store from './redux/store'

ReactDOM.render(
  <React.StrictMode>
      <Provider store={store}>
          <App />
      </Provider>
  </React.StrictMode>
,
  document.getElementById('root')
);

整合UI组件和容器组件

将UI组件和容器组件合并为一个文件。

准备工作:

React高级部分(二)

示例:

src/containers/Count/index.jsx

import React, {Component} from 'react';
//引入connect用于连接UI组件和redux
import {connect} from 'react-redux'
//引入action
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from '../../redux/count_action'

//定义UI组件
class Count extends Component {

    //加法
    increment = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        this.props.increment(parseInt(value));
    }

    //减法
    decrement = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        this.props.decrement(parseInt(value));
    }

    //当前求和为奇数才加
    incrementIfOdd = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        const {count} = this.props;
        if (count % 2 !== 0) {
            this.props.increment(parseInt(value));
        }
    }

    //异步加
    incrementAsync = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        this.props.incrementAsync(parseInt(value), 500);
    }

    render() {
        const {count} = this.props;
        return (
            <div>
                <h2>当前求和为:{count}</h2>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;&nbsp;
                <button onClick={this.increment}>+</button>
                &nbsp;&nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>

        );
    }
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(
    state => ({count: state}),
    /* mapDispatchToProps的精简写法 */
    {
        increment: createIncrementAction,
        decrement: createDecrementAction,
        incrementAsync: createIncrementAsyncAction
    }
)(Count);

react-redux实现数据共享

需求

使用react-redux实现数据共享。

React高级部分(二)

准备工作

目录结构:

React高级部分(二)

应用示例

示例:

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux'
import App from './App';
import store from './redux/store'

ReactDOM.render(
  <React.StrictMode>
      <Provider store={store}>
          <App />
      </Provider>
  </React.StrictMode>
,
  document.getElementById('root')
);

src/APP.jsx

import React, {Component} from 'react';
import Count from './containers/Count'
import Person from "./containers/Person";


export default class App extends Component {
    
    render() {
        return (
            <div>
                <Count/>
                <hr/>
                <Person/>
            </div>

        );
    }
}

src/redux/constant.js

/**
 * 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时,防止程序员单词写错
 */

export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';
export const ADD_PERSON = "add_person"

src/redux/store.js

/**
 * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
 */

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware,combineReducers} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './reducers/count'
//引入为Person组件服务的reducer
import personReducer from "./reducers/person";
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//汇总所有的reducer,变为一个总的reducer
const allReducers = combineReducers({
    count: countReducer,
    personArr: personReducer
})

//创建并暴露store对象
export default createStore(allReducers,applyMiddleware(thunk));

src/actions/count.js

/**
 * 该文件专门为Count组件生成action对象。
 */

import {INCREMENT, DECREMENT} from '../constant'

//同步action,就是指action的值为object类型的一般对象
export const createIncrementAction = data => ({type: INCREMENT, data: data})

export const createDecrementAction = data => ({type: DECREMENT, data: data})

//异步action,就是指action的值为函数,异步action一般都会调用同步action
export const createIncrementAsyncAction = (data, time) => {
    return (dispatch) => {
        setTimeout(() => {
            dispatch(createIncrementAction(data))
        }, time);
    };
}

src/redux/person.js

import {ADD_PERSON} from '../constant'

//创建增加一个人的Action对象
export const createAddPersonAction = data => ({type: ADD_PERSON, data})

src/reducers/count.js

import {INCREMENT, DECREMENT} from '../constant'

/**
 * 该文件是用于为Count组件服务的Reducer,Reducer的本质就是一个函数。
 * reducer函数会接收到两个参数,分别为:prevState(之前的对象)和action(动作对象)
 *
 * @param prevState 先前的状态,默认值为0
 * @param action 动作对象
 */

export default function countReducer(prevState = 0, action{
    //从action对象中获取type和data
    const {type, data} = action;
    //根据type决定如何加工数据
    switch (type) {
        //加法
        case INCREMENT:
            return prevState + data;
        //减法
        case DECREMENT:
            return prevState - data;
        //默认情况:没有匹配到,就是默认情况,就是reducer进行初始化
        default:
            return prevState;
    }
}

src/redux/person.js

import {ADD_PERSON} from '../constant'

export default function personReducer(prevState = [], action{
    const {type, data} = action;
    switch (type) {
        case ADD_PERSON:
            return [data,...prevState]
        default:
            return prevState;
    }
}

src/containers/Count/index.jsx

import React, {Component} from 'react';
//引入connect用于连接UI组件和redux
import {connect} from 'react-redux'
//引入action
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from '../../redux/actions/count'

//定义UI组件
class Count extends Component {

    //加法
    increment = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        this.props.increment(parseInt(value));
    }

    //减法
    decrement = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        this.props.decrement(parseInt(value));
    }

    //当前求和为奇数才加
    incrementIfOdd = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        const {count} = this.props;
        if (count % 2 !== 0) {
            this.props.increment(parseInt(value));
        }
    }

    //异步加
    incrementAsync = () => {
        //获取选中的值
        const {value} = this.selectNumber;
        this.props.incrementAsync(parseInt(value), 500);
    }

    render() {
        const {count, personCount} = this.props;
        return (
            <div>
                <h2>我是Count组件</h2>
                <h3>当前求和为:{count},下方组件总人数为:{personCount}</h3>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;&nbsp;
                <button onClick={this.increment}>+</button>
                &nbsp;&nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
                &nbsp;&nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>

        );
    }
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(
    state => ({count: state.count, personCount: state.personArr.length}),
    /* mapDispatchToProps的精简写法 */
    {
        increment: createIncrementAction,
        decrement: createDecrementAction,
        incrementAsync: createIncrementAsyncAction
    }
)(Count);

src/containers/Person/index.jsx

import React, {Component} from 'react';
import {connect} from 'react-redux'
import {nanoid} from 'nanoid'
import {createAddPersonAction} from '../../redux/actions/person'

class Person extends Component {

    add = () => {
        const name = this.nameNode.value;
        const age = this.ageNode.value;

        const obj = {
            id: nanoid(),
            name,
            age
        }
        this.props.add(obj);
        this.nameNode.value = '';
        this.ageNode.value = '';
    }

    render() {
        const {personArr, count} = this.props;
        return (
            <div>
                <h2>我是Person组件,上方组件求和为:{count}</h2>
                <input ref={c => this.nameNode = c} type="text" placeholder="请输入名称"/>
                <input ref={c => this.ageNode = c} type="text" placeholder="请输入年龄"/>
                <button onClick={this.add}>添加</button>
                <ul>
                    {
                        personArr.map(p => {
                            return (<li key={p.id}>{p.name}---{p.age}</li>)
                        })
                    }
                </ul>
            </div>

        );
    }
}

export default connect(
    //映射状态
    state => ({personArr: state.personArr, count: state.count}),
    //映射状态的操作方法
    {
        add: createAddPersonAction
    }
)(Person)

纯函数和高阶函数

纯函数

一类特别的函数:只要是同样的输入(实参),必定得到同样的输出(返回)。

必须遵守以下的约束。

  • ① 不得改写参数数据。
  • ② 不会产生任何副作用,例如:网络请求、输入和输出设备。
  • ③ 不能调用Date.now()或Math.random()等不纯的方法。

redux的reducer函数必须是一个纯函数。

高阶函数

一类特别的函数:参数是函数,返回值是函数。

常见的高阶函数:

  • ① 定时器设置函数。
  • ② 数组的forEach()、map()、filter()、reduce()、find()、bind()等。
  • ③ Promise。
  • ④ react-redux中的connect函数。

作用:能实现更加动态、更加可扩展的功能。

使用redux调试工具

安装Chrome浏览器插件

React高级部分(二)

下载工具依赖包

命令:

yarn add redux-devtools-extension

示例:

在store.js中配置redux-devtools-extension。

/**
 * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
 */

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware,combineReducers} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './reducers/count'
//引入为Person组件服务的reducer
import personReducer from "./reducers/person";
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//引入redux-devtools-extension
import {composeWithDevTools} from 'redux-devtools-extension'
//汇总所有的reducer,变为一个总的reducer
const allReducers = combineReducers({
    count: countReducer,
    personArr: personReducer
})

//创建并暴露store对象 applyMiddleware(thunk)
export default createStore(allReducers,composeWithDevTools(applyMiddleware(thunk)));

React的扩展

setState

对象式的setState

语法:

setState(stateChange[,callback]);

解释:

  • stateChange:状态改变对象(该对象可以体现出状态的更改)。
  • callback:可选的回调函数,它在状态更新完毕,并且界面更新后(render调用后)才被调用。

示例:

React高级部分(二)

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/App.jsx

import React, { Component } from 'react';
import Demo from "./components/Demo";

export default class App extends Component {

    render() {
        return (
            <div>
                <Demo />
            </div>

        );
    }
}

src/components/Demo/index.jsx

import React, {Component} from 'react';

export default class Demo extends Component {

    state = {
        count0
    }

    addCount = () => {
        //①获取原来的count值
        const {count} = this.state;
        //②更新状态 异步执行的
        this.setState({count: count + 1}, function ({
            console.log('回调函数:'this.state.count);//1
        });
        console.log(this.state.count);//0
    }

    render() {
        return (
            <div>
                <h2>当前求和为:{this.state.count}</h2>
                <button onClick={this.addCount}>点我加1</button>
            </div>

        );
    }
}

函数式的setState

语法:

setState(updater[,callback]);

解释:

  • updater为返回stateChange对象的函数。
  • updater可以接收到state和props。
  • callback:可选的回调函数,它在状态更新完毕,并且界面更新后(render调用后)才被调用。

示例:

React高级部分(二)

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/APP.jsx

import React, { Component } from 'react';
import Demo from "./components/Demo";

export default class App extends Component {

    render() {
        return (
            <div>
                <Demo x={101}/>
            </div>

        );
    }
}

src/components/Demo/index.jsx

import React, {Component} from 'react';

export default class Demo extends Component {

    state = {
        count0
    }

    addCount = () => {
        /* 对象式的setState */
        /*
            //①获取原来的count值
            const {count} = this.state;
            //②更新状态 异步执行的
            this.setState({count: count + 1}, function () {
                console.log('回调函数:', this.state.count);//1
            });
            console.log(this.state.count);//0
        */

        /* 函数式的setState */
        this.setState((state, props) => {
            console.log(state, props);
            return {count: state.count + 1};
        })
    }

    render() {
        return (
            <div>
                <h2>当前求和为:{this.state.count}</h2>
                <button onClick={this.addCount}>点我加1</button>
            </div>

        );
    }
}

总结

对象式的setState是函数式的setState的简写方式(语法糖)。

使用原则:

  • ① 如果新状态不依赖于原状态,使用对象方式。
  • ② 如果新状态依赖于原状态,使用函数方式。
  • ③ 如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中读取。

lazyLoad

一般路由组件需要lazyLoad。

使用步骤:

① 通过react的lazy函数配置import()函数动态加载路由组件(路由组件代码会被分开打包)。

const Login = lazy(()=>import('@/pages/Login'))

② 通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面。

<Suspense fallback={<div>loading.....</div>}>
    <Switch>
        <Route path="/
xxx" component={Xxxx}/>
        <Redirect to="
/login"/>
    </Switch>
</Suspense>

示例:

React高级部分(二)

安装路由:

yarn add react-route-dom

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {BrowserRouter} from "react-router-dom";


ReactDOM.render(
    <React.StrictMode>
        <BrowserRouter>
            <App/>
        </BrowserRouter>
    </React.StrictMode>
,
    document.getElementById('root')
);

src/App.jsx

import React, {Component, lazy,Suspense} from 'react';
import {NavLink, Route, Switch} from 'react-router-dom';
import Header from "./components/Header";
const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))

export default class App extends Component {

    render() {
        return (
            <div>
                <div className="row">
                    <div className="col-xs-offset-2 col-xs-8">
                        <Header/>
                    </div>
                </div>
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            <NavLink className="list-group-item" activeClassName="active" to="/about">About</NavLink>
                            <NavLink className="list-group-item" activeClassName="active" to="/home">Home</NavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                <Suspense fallback={<div>loading.....</div>}>
                                    <Switch>
                                        <Route path="/about" component={About}/>
                                        <Route path="/home" component={Home}/>
                                    </Switch>
                                </Suspense>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

        );
    }
}

src/components/Header/index.jsx

import React, {Component} from 'react';

export default class Header extends Component {
    render() {
        return (
            <div className="page-header"><h2>React Router Demo</h2></div>
        );
    }
}

src/pages/Home/index.jsx

import React, {Component} from 'react';

export default class Home extends Component {
    render() {
        return (
            <h3>我是Home的内容</h3>
        );
    }
}

src/pages/About/index.jsx

import React, {Component} from 'react';

export default class About extends Component {
    render() {
        return (
            <h3>我是About的内容</h3>
        );
    }
}

Hooks

概述

Hooks是React 16.8.0版本增加的新特性/新语法。

可以让你在函数式组件中使用state以及其他的React特性。

常用的Hook

  • State Hook:React.useState()。
  • Effect Hook:React.useEffect()。
  • Ref Hook:React.useRef()。

State Hook

State Hook可以让函数式组件也有state状态,并进行状态数据的读写操作。

语法:

const [xxx,setXxx] = React.useState(initValue);

解释:

  • useState()的参数:第一次初始化指定的值在内存作为缓存。
  • useState()的返回值:包含2个元素的数组,第一个为内部当前状态,第二个位更新状态值的函数。

setXxx()的2种写法:

  • setXxx(newValue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值。
  • setXxx(value=>newValue):参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值。

示例:

React高级部分(二)

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/App.jsx

import React, { Component } from 'react';
import Demo from "./components/Demo";

export default class App extends Component {

    render() {
        return (
            <div>
                <Demo />
            </div>

        );
    }
}

src/components/Demo/index.jsx

import React from 'react'

export default function Demo({

    const [count, setCount] = React.useState(0);
    const [name, setName] = React.useState('许大仙是谁?');

    //加的回调
    function add({
        // setCount(count + 1); 第一种写法
        setCount(count => count + 1);
    }

    function change({
        setName('是你大爷');
    }

    return (
        <div>
            <h2>当前求和为:{count}</h2>
            <h2>我的名字是:{name}</h2>
            <button onClick={add}>点我加1</button>
            <button onClick={change}>点我改名字</button>
        </div>

    )
}

Effect Hook

Effect Hook可以让我们在函数组件中执行副作用操作(用于模拟两类组件中的生命周期钩子)。

Effect中的副作用操作:

  • ① 发送Ajax请求数据获取。
  • ② 设置订阅/启动定时器。
  • ③ 手动更改真实DOM。

语法:

React.useEffect(()=>{
    //在此可以执行任何带副作用的操作
    return ()=>//在组件卸载前执行
        //在此做一些收尾工作,比如清除定时器、取消订阅等
    }
}[,stateValue]) //如果指的是[],回调函数只会在第一次render()后执行

可以将Effect Hook看做如下三个函数的组合。

  • ① componentDidMount()。
  • ② componentDidUpdate()。
  • ③ componentWillUnmount() 。

示例:

React高级部分(二)

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/App.jsx

import React, { Component } from 'react';
import Demo from "./components/Demo";

export default class App extends Component {

    render() {
        return (
            <div>
                <Demo />
            </div>

        );
    }
}

src/components/Demo/index.jsx

import React from 'react'
import ReactDOM from 'react-dom'

export default function Demo({

    const [count, setCount] = React.useState(0);
    const [name, setName] = React.useState('许大仙是谁?');

    React.useEffect(() => {
        //在此可以执行任何带副作用操作
        let timer = setInterval(() => {
            setCount(count => count + 1);
        }, 1000);
        return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
            clearInterval(timer);
        }
    }, []); //如果指的是[],回调函数只会在第一次render()后执行

    //加的回调
    function add({
        // setCount(count + 1); 第一种写法
        setCount(count => count + 1);
    }

    //修改名称的回调
    function change({
        setName('是你大爷');
    }

    //卸载组件的回调
    function unmount(){
        ReactDOM.unmountComponentAtNode(document.getElementById('root'));
    }

    return (
        <div>
            <h2>当前求和为:{count}</h2>
            <h2>我的名字是:{name}</h2>
            <button onClick={add}>点我加1</button>
            <button onClick={change}>点我改名字</button>
            <button onClick={unmount}>卸载组件</button>
        </div>

    )
}

Ref Hook

Ref Hook可以在函数式组件中存储/查找组件内的标签或其他任意数据。

语法:

const refContainer = React.useRef()

作用:保存标签对象,功能和React.createRef()一样。

示例:

React高级部分(二)

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/App.jsx

import React, { Component } from 'react';
import Demo from "./components/Demo";

export default class App extends Component {

    render() {
        return (
            <div>
                <Demo />
            </div>

        );
    }
}

src/components/Demo/index.jsx

import React from 'react'

export default function Demo({

    const [count, setCount] = React.useState(0);
    const [name, setName] = React.useState('许大仙是谁?');

    const myRef = React.useRef();

    //加的回调
    function add({
        // setCount(count + 1); 第一种写法
        setCount(count => count + 1);
    }

    //修改名称的回调
    function change({
        setName('是你大爷');
    }

    //提示输入的回调
    function show({
        console.log(myRef.current.value);
    }

    return (
        <div>
            <input type="text" ref={myRef}/>
            <h2>当前求和为:{count}</h2>
            <h2>我的名字是:{name}</h2>
            <button onClick={add}>点我加1</button>
            <button onClick={change}>点我改名字</button>
            <button onClick={show}>点我提示数据</button>
        </div>

    )
}

Fragment

Fragment可以不用再使用一个真实的DOM根标签了。

语法:

import React, {Component,Fragment} from 'react';

<Fragment>
   //
<Fragment>

示例:

React高级部分(二)

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/App.jsx

import React, { Component, Fragment } from 'react';
import Demo from "./components/Demo";

export default class App extends Component {

    render() {
        return (
            <Fragment>
                <Demo />
            </Fragment>

        );
    }
}

src/component/Demo/index.jsx

import React, {Component,Fragment} from 'react';

export default class Demo extends Component {
    render() {
        return (
            <Fragment key={1}>
                <input type="text"/>
                <input type="text"/>
            </Fragment>

        );
    }
}

Context

概述

一种组件通信方式,常用于【祖先组件】和【后代组件】间的通信。

使用

① 创建Context容器对象,需要让祖先组件和后代组件都能访问到。

const XxxContext = React.createContext();

② 渲染子组件时,外面包裹XxxContext.Provider,通过value属性给后代组件传递数据。

<XxxContext.Provider value={数据}>
    子组件
</XxxContext.Provider>

③ 后代组件读取数据:

第一种方式:仅适用于类组件。

//声明接收context
static contextType = XxxContext;

//读取context中的数据
this.context

第二种方式:函数式组件和类式组件都可以。

<XxxContext.Consumer>
{
    value => { //value就是context中的value数据
        //要显示的内容
    }
}
</XxxContext.Consumer>

注意:在实际开发的时候一般不用conetxt,一般都用它封装React插件。

示例:

React高级部分(二)

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/App.jsx

import React, { Component, Fragment } from 'react';
import Demo from "./components/Demo";

export default class App extends Component {

    render() {
        return (
            <Fragment>
                <Demo />
            </Fragment>

        );
    }
}

src/components/Demo/index.jsx

import React, {Component} from 'react';

//创建Context对象
const MyContext = React.createContext();

export default class Demo extends Component {
    state = {username'许大仙'age18}

    render() {
        const {username, age} = this.state;
        return (
            <div style={{backgroundColor: 'orange', padding: '8px'}}>
                <h2>我是Demo组件</h2>
                <h4>我的用户名是:{username}</h4>
                <MyContext.Provider value={{username, age}}>
                    <B/>
                </MyContext.Provider>
            </div>

        );
    }
}


class B extends Component {
    render() {
        return (
            <div style={{backgroundColor: 'skyblue', padding: '8px'}}>
                <h2>我是B组件</h2>
                <C/>
                <D/>
            </div>

        );
    }
}

class C extends Component {

    //声明接收context
    static contextType = MyContext;

    render() {
        console.log(this.context);
        const {username, age} = this.context;
        return (
            <div style={{backgroundColor: 'gray', padding: '8px'}}>
                <h2>我是C组件</h2>
                <h4>我从A组件接收到的用户名是:{username},年龄是:{age}</h4>
            </div>

        );
    }
}

function D({
    return (
        <div style={{backgroundColor: 'pink', padding: '8px'}}>
            <h2>我是D组件</h2>
            <h4>
                我从A组件接收到的用户名是:
                <MyContext.Consumer>
                    {
                        value => {
                            return `${value.username},年龄是${value.age}`
                        }
                    }
                </MyContext.Consumer>
            </h4>
        </div>

    );
}

组件优化

component的两个问题

① 只要执行了setState(),即使不改变状态数据,组件也会重新render(),这样导致效率低。

② 只要父组件重新render(),就会自动重新render子组件,即使子组件没有用到父组件的任何数据,这样也导致效率低。

示例:

React高级部分(二)

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/App.jsx

import React, { Component, Fragment } from 'react';
import Demo from "./components/Demo";

export default class App extends Component {

    render() {
        return (
            <Fragment>
                <Demo />
            </Fragment>

        );
    }
}

src/components/Demo/index.css

.parent {
    background-color: orange;
    padding10px;
}

.child {
    background-color: grey;
    padding10px;
    margin-top30px;
}

src/components/Demo/index.jsx

import React, {Component} from 'react';
import './index.css'

export default class Parent extends Component {

    state = {
        carName'奔驰'
    }

    changeCar = () => {
        this.setState({})
    }

    render() {
        console.log('Parent...')
        return (
            <div className="parent">
                <h3>我是Parent组件</h3>
                <span>我的车名字是{this.state.carName}</span><br/>
                <button onClick={this.changeCar}>点我换车</button>
                <Child />
            </div>

        );
    }
}


class Child extends Component {
    render() {
        console.log('Child...')
        return (
            <div className="child">
                <h3>我是Child组件</h3>
                {/*<span>我接收到的车是{this.props.carName}</span>*/}
            </div>

        );
    }
}

原因

Component中的shouldComponentUpdate()总是返回true。

效率高的做法

只有当组件的state或props数据发生改变时才重新render()。

解决方案

第一种解决方案:

  • 重写shouldComponentUpdate()方法,比较新旧state或props数据,如果有变化才返回true;否则,返回false。

第二种解决方案:

  • 使用PureComponent。
  • PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true。

注意:

只是进行state或props数据的浅比较,如果只是数据对象内部数据变了,返回false。

不要直接修改state数据,而是要产生新的数据。

总结:项目中一般使用PureComponent来优化。

示例:

src/components/Demo/index.jsx

import React, {PureComponent} from 'react';
import './index.css'

export default class Parent extends PureComponent {

    state = {
        carName'奔驰'
    }

    changeCar = () => {
        this.setState({})
    }

    render() {
        console.log('Parent...')
        return (
            <div className="parent">
                <h3>我是Parent组件</h3>
                <span>我的车名字是{this.state.carName}</span><br/>
                <button onClick={this.changeCar}>点我换车</button>
                <Child carName={"奥拓"}/>
            </div>

        );
    }
}


class Child extends PureComponent {
    render() {
        console.log('Child...')
        return (
            <div className="child">
                <h3>我是Child组件</h3>
                <span>我接收到的车是{this.props.carName}</span>
            </div>

        );
    }
}

render props

概述

向组件内部动态传入带内容的结构(标签):

  • Vue中:使用solt技术,也就是通过组件标签体传入结构(<A><B/></A>)。

  • React中:

    • 使用children pops:通过组件标签体传入结构。
    • 使用render props:通过组件标签属性传入结构,而且可以携带数据,一般使用render函数属性。

children props

语法:

<A>
 <B/>
</A>

/
/ 在A中通过{this.props.children渲染B}

问题:如果B组件需要A组件传入数据,这种方式做不到。

示例:

React高级部分(二)

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/App.jsx

import React, { Component, Fragment } from 'react';
import Demo from "./components/Demo";

export default class App extends Component {

    render() {
        return (
            <Fragment>
                <Demo />
            </Fragment>

        );
    }
}

src/components/Demo/index.css

.parent {
    background-color: orange;
    padding10px;
}

.a {
    background-color: grey;
    padding10px;
    margin-top30px;
}

.b {
    background-color: skyblue;
    padding10px;
    margin-top30px;
}

src/components/Demo/index.jsx

import React, {Component} from 'react';
import './index.css'

export default class Parent extends Component {

    render() {
        return (
            <div className="parent">
                <h3>我是Parent组件</h3>
                <A>
                    <B/>
                </A>
            </div>

        );
    }
}


class A extends Component {

    state = {name'tom'}

    render() {
        return (
            <div className="a">
                <h3>我是A组件</h3>
                {this.props.children}
            </div>

        );
    }
}

class B extends Component {
    render() {
        return (
            <div className="b">
                <h3>我是B组件,{this.props.name}</h3>
            </div>

        );
    }
}

render props

语法:

<A render={(data) => <B data={data}></B>}></A>

/
/A组件:{this.props.render(内部state数据)}
/
/B组件:读取A组件传入的数据显示 {this.props.data}

示例:

src/components/Demo/index.jsx

import React, {Component} from 'react';
import './index.css'

export default class Parent extends Component {

    render() {
        return (
            <div className="parent">
                <h3>我是Parent组件</h3>
                <A render={(name) => <B name={name}/>}/>
            </div>

        );
    }
}


class A extends Component {

    state = {name'tom'}

    render() {
        return (
            <div className="a">
                <h3>我是A组件</h3>
                {this.props.render(this.state.name)}
            </div>

        );
    }
}

class B extends Component {
    render() {
        return (
            <div className="b">
                <h3>我是B组件,{this.props.name}</h3>
            </div>

        );
    }
}

错误边界

概述

错误边界(Error Boundary):用来捕获后代组件错误,渲染出备用页面。

特点

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误。

使用方式

getDerivedStateFromError配合componentDidCatch。

// 生命周期函数,一旦后代组件报错,就会触发
static getDerivedStateFromError(error) {
    console.log(error);
    // 在render之前触发
    // 返回新的state
    return {
        hasErrortrue,
    };
}

componentDidCatch(error, info) {
    // 统计页面的错误,发送请求到后台去
    console.log(error, info);
}

应用示例

示例:

React高级部分(二)

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
,
  document.getElementById('root')
);

src/App.jsx

import React, { Component, Fragment } from 'react';
import Parent from "./components/Parent";

export default class App extends Component {

    render() {
        return (
            <Fragment>
                <Parent />
            </Fragment>

        );
    }
}

src/component/Child/index.jsx

import React, {Component} from 'react';

export default class Child extends Component {

    state = {
        // users: [
        //     {id: '001', name: 'tom', age: 18},
        //     {id: '002', name: 'jerry', age: 19},
        //     {id: '003', name: 'andy', age: 20}
        // ]
        users'abc'
    }

    render() {
        return (
            <div>
                <h2>我是Child组件</h2>
                {
                    this.state.users.map(user => {
                        return (
                            <h4 key={user.id}>{user.name}----{user.age}</h4>
                        )
                    })
                }
            </div>

        );
    }
}

src/component/Parent/index.jsx

import React, {Component} from 'react';
import Child from "../Child";

export default class Parent extends Component {

    state = {
        hasError'' //用于标识子组件是否产生错误
    }


    //当Parent的子组件出现报错时,会触发getDerivedStateFromError的调用,并携带错误信息
    static getDerivedStateFromError(error) {
        console.log('getDerivedStateFromError', error);
        return {hasError: error};
    }

    componentDidCatch(error, info) {
        // 此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决
        console.log('componentDidCatch', error, info);
    }

    render() {
        return (
            <div>
                <h2>我是Parent组件</h2>
                {
                    this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child/>
                }
            </div>

        );
    }
}

组件通信方式总结

组件间的关系

  • 父子组件。
  • 兄弟组件(非嵌套组件)。
  • 祖先后代组件(跨级组件)。

通信方式

props:

  • children props。
  • render props。

消息订阅和发布:

  • pubs-sub、event等等。

集中式管理:

  • redux、dva等等。

Context:生产者-消费者模式。

比较好的搭配

  • 父子组件:props。
  • 兄弟组件:消息订阅-发布、集中式管理。
  • 祖先后代组件(跨级组件):消息订阅和发布、集中式管理、Context(开发用的比较少,封装插件用的多)。


原文始发于微信公众号(程序员阿晶):React高级部分(二)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/19862.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!