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
备注:
Y
代表yarn。
N
代表npm。
*
代表npm和yarn共同的源。
切换源:
cgr use 源名称 [类型,y/yarn表示yarn切换;n/npm表示npm切换;为空,表示同时切换源]
添加源:
cgr add 源名称 源地址
删除源:
cgr del 源名称
源响应时间测试:
cgr test
安装React脚手架和初始化React项目
全局安装:
npm -g install create-react-app
切换到你想创建项目的目录:
create-react-app antd
进入项目文件夹:
cd antd
启动项目:
yarn run start
准备工作
目录结构:
引入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', style: true }],
]
},
};
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" },
javascriptEnabled: true,
},
},
},
},
],
babel: {
plugins: [
['import', { libraryName: 'antd', style: true }],
]
},
};
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的工作流程
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版求和案例
准备工作
目录结构:
应用示例
示例:
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 = {
count: 0
}
//加法
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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
);
}
}
redux精简版求和案例
准备工作
目录结构:
安装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',
data: parseInt(value)
})
}
//减法
decrement = () => {
//获取选中的值
const {value} = this.selectNumber;
//分发action,触发reducer调用,产生新的state
store.dispatch({
type: 'decrement',
data: parseInt(value)
})
}
//当前求和为奇数才加
incrementIfOdd = () => {
//获取选中的值
const {value} = this.selectNumber;
//获取redux中维护的state
let count = store.getState();
if (count % 2 !== 0) {
store.dispatch({
type: 'increment',
data: parseInt(value)
})
}
}
//异步加
incrementAsync = () => {
setTimeout(() => {
//获取选中的值
const {value} = this.selectNumber;
//分发action,触发reducer调用,产生新的state
store.dispatch({
type: 'increment',
data: 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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
);
}
}
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/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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
);
}
}
redux的异步编程
概述
redux默认是不能进行异步处理的。
某些时候应用中需要在redux中执行异步任务(ajax、定时器)。
需要借助中间件redux-thunk:
yarn add redux-thunk
准备工作
目录结构:
应用示例
示例:
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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
<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-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的基本使用
准备工作:
示例:
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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
);
}
}
mapDispatchToProps的优化
准备工作:
示例:
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数据。
准备工作:
示例:
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组件和容器组件合并为一个文件。
准备工作:
示例:
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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
<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实现数据共享。
准备工作
目录结构:
应用示例
示例:
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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数才加</button>
<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浏览器插件
下载工具依赖包
命令:
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调用后)才被调用。
示例:
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 = {
count: 0
}
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调用后)才被调用。
示例:
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 = {
count: 0
}
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>
示例:
安装路由:
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):参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值。
示例:
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() 。
示例:
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()
一样。
示例:
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>
示例:
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插件。
示例:
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: '许大仙', age: 18}
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子组件,即使子组件没有用到父组件的任何数据,这样也导致效率低。
示例:
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;
padding: 10px;
}
.child {
background-color: grey;
padding: 10px;
margin-top: 30px;
}
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组件传入数据,这种方式做不到。
示例:
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;
padding: 10px;
}
.a {
background-color: grey;
padding: 10px;
margin-top: 30px;
}
.b {
background-color: skyblue;
padding: 10px;
margin-top: 30px;
}
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 {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误,发送请求到后台去
console.log(error, info);
}
应用示例
示例:
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