react 状态机制
虽然 react 前面使用通过创建一个 context【上下链接】对象,基于 Provider【供应者】—— Consumer【消费者】来进行数据的获取,但这种方式局限于只能读取数据,对数据的其他操作还是需要通过 props 属性来操作
可以说,这种感觉就像带着女票去宾馆,买的是胶体 BYT,虽然完美的起到了保护措施,解决了后顾之忧,但中间还是有那么一点缺真实感
BYT 的厂家为了给用户多一点真实感,生产出一种液体 BYT,sou~,为了完美的体验,当然首选液体 BYT 啦~
随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态), 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等
管理不断变化的 state 非常困难,如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么,state 在什么时候,由于什么原因,如何变化已然不受控制, 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰
这里的复杂性很大程度上来自于:总是将两个难以理清的概念混淆在一起:变化和异步。 我称它们为曼妥思和可乐。如果把二者分开,能做的很好,但混到一起,就变得一团糟。一些库如 React 试图在视图层禁止异步和直接操作 DOM 来解决这个问题。美中不足的是,React 依旧把处理 state 中数据的问题留给了你,Redux就是为了帮你解决这个问题
Components
组件这个客户要去 Store
超市买东西,在超市门口花了一块钱搞了辆购物车 Action
,进入超市后,由于超市很大,我们需要去不同产品区 Reducers
找寻所需的商品进行购买
三大原则:
-
单一数据源:整个应用的
state
被储存在一棵object tree
中,且只存在于唯一一个store
中 -
状态是只读:唯一改变
state
的方法就是触发action
,action
是一个用于描述已发生事件的普通对象 -
纯函数执行:为了描述
action
如何改变state tree
,你需要编写reducers
Store
就是把它们联系到一起的对象。Store
有以下职责:
- 维持应用的 state
- 提供 getState() 方法获取 state
- 提供 dispatch(action) 方法更新 state
- 通过 subscribe(listener) 注册监听器
- 通过 subscribe(listener) 返回的函数注销监听器
Reducers
指定了应用状态的变化如何响应 actions
并发送到 store
的,记住 actions
只是描述了有事情发生了这一事实,并没有描述应用如何更新 state
Action
是把数据从应用传到 store
的有效载荷,它是 store
数据的唯一来源
下面通过 redux
状态管理创建基于 react + antd
框架的 todolist
项目
$ npx create-react-app todo-list
$ cd react-demo
$ npm install antd --save
$ npm install babel-plugin-import --save-dev
$ npm install redux --save
$ npm run eject
调整目录结构,删除不需要的文件,修改根目录下的 package.json
的 babel
处,在 persets 后面添加
|-- config
|-- node_modules
|-- public
|-- script
|-- src
|-- components
|-- redux
|-- app.css
|-- App.js
|-- index.js
|-- ...
index.js
代码如下:引入 antd
中文包,并设置 antd
全局中文语言设定
import React from 'react';
import ReactDOM from 'react-dom';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/es/locale/zh_CN'; // 引入中文包
import App from './App';
ReactDOM.render(
<React.StrictMode>
<ConfigProvider locale={zhCN}>
<App />
</ConfigProvider>
</React.StrictMode>,
document.getElementById('root')
);
配置 package.json
代码如下:设置 antd
按需加载,避免代码臃肿
{
......
"babel": {
"presets": [
"react-app"
],
"plugins": [
[
"import",
{
"libraryName": "antd",
"libraryDirectory": "es",
"style": "css"
}
]
]
},
......
}
App.js
代码如下:用于查看 antd
是否为 按需加载
import React, { Component } from 'react';
import { Button } from 'antd';
class App extends Component {
constructor(props) {
super(props);
this.state = { }
}
render() {
return (
<div>
<Button type="primary">Primary Button</Button>
<Button>Default Button</Button>
<Button type="dashed">Dashed Button</Button>
<br />
<Button type="text">Text Button</Button>
<Button type="link">Link Button</Button>
</div>
);
}
}
export default App;
基于页面可以分三个组件:头部 ButtonList
按钮组合组件、中间 TableList
表格列表组件、底部 TotalList
统计数据组件,那么在 components
下创建这三个组件
|-- components
|-- ButtonList
|-- index.jsx
|-- TableList
|-- index.jsx
|-- TotalList
|-- index.jsx
ButtonList/index.jsx
、TableList/index.jsx
、TotalList/index.jsx
、App.js
代码如下
import React, { useState } from 'react';
import { Modal, Button, Form, Input } from 'antd';
export default function ButtonList (props) {
const [isModalVisible, setModalVisible] = useState(false);
const [form] = Form.useForm();
const showModal = () => {
setModalVisible(true);
};
const hideModal = () => {
setModalVisible(false);
};
const handleOk = () => {
}
return (
<>
<div style={{ position: 'absolute', top: '16px', zIndex: 1 }}>
<Button type="primary" danger style={{marginRight: '20px'}}>全部删除</Button>
<Button type="primary" onClick={showModal}>新增</Button>
</div>
<Modal title="Basic Modal" visible={isModalVisible} onOk={handleOk} onCancel={hideModal}>
<Form layout={{ labelCol: { span: 8 }, wrapperCol: { span: 16 } }} form={form} name="control-hooks">
<Form.Item name="name" label="姓名" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="yuwen" label="语文" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="shuxue" label="数学" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="yingyu" label="英语" rules={[ { required: true } ]} >
<Input />
</Form.Item>
</Form>
</Modal>
</>
)
}
import React, { useState } from 'react';
import { Table, Space } from 'antd';
export default function TableList (props) {
const { selectedRowKeys } = props;
const columns = [
{
title: '姓名',
dataIndex: 'name',
},
{
title: '语文',
dataIndex: 'yuwen',
sorter: {
compare: (a, b) => a.yuwen - b.yuwen,
multiple: 3,
},
},
{
title: '数学',
dataIndex: 'shuxue',
sorter: {
compare: (a, b) => a.shuxue - b.shuxue,
multiple: 2,
},
},
{
title: '英语',
dataIndex: 'yingyu',
sorter: {
compare: (a, b) => a.yingyu - b.yingyu,
multiple: 1,
},
},
{
title: '操作',
render: (text, record) => (
<Space>
<a>删除</a>
</Space>
),
}
];
const rowSelection = {
selectedRowKeys,
onChange: selectedRowKeys => {},
};
let datalist = [];
return (
<Table
columns={columns} dataSource={datalist}
pagination={{position: ['topRight', 'none']}}
rowSelection={rowSelection}
/>
)
}
import { Row, Col } from 'antd';
export default function TotalList (props) {
const { datalist } = props, len = datalist.length;
let totalyw = 0, totalsx = 0, totalyy = 0;
datalist.map(item => {
totalyw += item.yuwen;
totalsx += item.shuxue;
totalyy += item.yingyu;
})
return (
<Row style={{lineHeight: '40px', borderBottom: '1px solid #cccccc'}}>
<Col span={4} style={{marginRight: '26px', paddingLeft: '20px'}}>平均分</Col>
<Col span={4} style={{marginRight: '34px'}}>{totalyw / len}</Col>
<Col span={4} style={{marginRight: '36px'}}>{totalsx / len}</Col>
<Col span={4}>{totalyy / len}</Col>
<Col span={4}></Col>
</Row>
)
}
import React, { Component } from 'react';
import './app.css';
import TableList from './components/TableList/index';
import ButtonList from './components/ButtonList/index';
import TotalList from './components/TotalList/index';
class App extends Component {
constructor(props) {
super(props);
this.state = { datalist: [], selectedRowKeys: [] };
}
render() {
const { datalist, selectedRowKeys } = this.state;
return (
<div className="view-main">
<ButtonList datatotal={datalist.length} />
<TableList datalist={datalist} selectedRowKeys={selectedRowKeys} />
<TotalList datalist={datalist} />
</div>
);
}
}
export default App;
以上就是通过 antd
生成的 todolist
页面效果,现在在 redux
文件夹内创建 store.js
、reducer.js
、action.js
,store.js
是唯一的,用来创建唯一的 Store
对象,传递的一定是一个 reducer
函数
import { createStore } from 'redux';
import reducer from './reducer';
export default createStore(reducer);
reducer.js
是用来解析 store
和更新 store
的函数
const defaultState = {
datalist: [], // todolist 中间表格列表数据
selectedRowKeys: [], // 用于判定选中项目,方便删除多列判定
};
export default (state = defaultState, action) => {
// 由于对象是引用数据类型,需要通过深拷贝赋值新对象,而此处解构的原因是为了做深拷贝
let newState = { ...state };
return newState;
}
通过设置默认状态数据,查看 redux
配置是否成功
const defaultState = {
datalist: [
{ name: '小明', yuwen: 87, shuxue: 95, yingyu: 87 },
{ name: '小花', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '小红', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '小李', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '天天', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '菲菲', yuwen: 84, shuxue: 96, yingyu: 92 },
], // todolist 中间表格列表数据
selectedRowKeys: [], // 用于判定选中项目,方便删除多列判定
};
export default (state = defaultState, action) => {
// 由于对象是引用数据类型,需要通过深拷贝赋值新对象,而此处解构的原因是为了做深拷贝
let newState = { ...state };
return newState;
}
import React, { Component } from 'react';
import './app.css';
import store from './redux/store';
import TableList from './components/TableList/index';
import ButtonList from './components/ButtonList/index';
import TotalList from './components/TotalList/index';
class App extends Component {
constructor(props) {
super(props);
let { datalist, selectedRowKeys } = store.getState();
this.state = { datalist, selectedRowKeys };
}
render() {
const { datalist, selectedRowKeys } = this.state;
return (
<div className="view-main">
<ButtonList datatotal={datalist.length} />
<TableList datalist={datalist} selectedRowKeys={selectedRowKeys} />
<TotalList datalist={datalist} />
</div>
);
}
}
export default App;
通过设置新增状态数据,查看 redux
是否新增成功,状态是否更新
import React, { useState } from 'react';
import { Modal, Button, Form, Input } from 'antd';
export default function ButtonList (props) {
const [isModalVisible, setModalVisible] = useState(false);
const [form] = Form.useForm();
const { dispatch } = props;
const showModal = () => {
setModalVisible(true);
};
const hideModal = () => {
setModalVisible(false);
};
const handleOk = () => {
form.validateFields().then(res => {
dispatch({ type: 'inputItem', value: form.getFieldsValue() });
setModalVisible(false);
});
}
return (
<>
<div style={{ position: 'absolute', top: '16px', zIndex: 1 }}>
<Button type="primary" danger style={{marginRight: '20px'}} onClick={() => dispatch({ type: 'deleteList' })}>全部删除</Button>
<Button type="primary" onClick={showModal}>新增</Button>
</div>
<Modal title="Basic Modal" visible={isModalVisible} onOk={handleOk} onCancel={hideModal}>
<Form layout={{ labelCol: { span: 8 }, wrapperCol: { span: 16 } }} form={form} name="control-hooks">
<Form.Item name="name" label="姓名" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="yuwen" label="语文" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="shuxue" label="数学" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="yingyu" label="英语" rules={[ { required: true } ]} >
<Input />
</Form.Item>
</Form>
</Modal>
</>
)
}
import React, { Component } from 'react';
import './app.css';
import store from './redux/store';
import TableList from './components/TableList/index';
import ButtonList from './components/ButtonList/index';
import TotalList from './components/TotalList/index';
class App extends Component {
constructor(props) {
super(props);
let { datalist, selectedRowKeys } = store.getState();
this.state = { datalist, selectedRowKeys };
}
componentDidMount () {
// 需要通过 store 的订阅功能进行视图数据更新
store.subscribe(() => this.setState(store.getState()));
}
render() {
const { datalist, selectedRowKeys } = this.state;
return (
<div className="view-main">
<ButtonList datatotal={datalist.length} dispatch={store.dispatch} />
<TableList datalist={datalist} selectedRowKeys={selectedRowKeys} />
<TotalList datalist={datalist} />
</div>
);
}
}
export default App;
通过 store.dispatch
进行事务的传递,通过 store.subscribe
订阅给界面状态更新数据
const defaultState = {
datalist: [
{ name: '小明', yuwen: 87, shuxue: 95, yingyu: 87 },
{ name: '小花', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '小红', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '小李', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '天天', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '菲菲', yuwen: 84, shuxue: 96, yingyu: 92 },
],
selectedRowKeys: [], // 用于判定选中项目,方便删除多列判定
};
export default (state = defaultState, action) => {
// 由于对象是引用数据类型,需要通过深拷贝赋值新对象,而此处解构的原因是为了做深拷贝
let newState = { ...state };
switch (action.type) {
case 'inputItem':
newState.datalist = newState.datalist.concat([action.value]);
break;
}
return newState;
}
通过设置删除状态数据,查看 redux
是否删除成功,状态是否更新
import React, { useState } from 'react';
import { Table, Space } from 'antd';
export default function TableList (props) {
const { selectedRowKeys, dispatch } = props;
const columns = [
{
title: '姓名',
dataIndex: 'name',
},
{
title: '语文',
dataIndex: 'yuwen',
sorter: {
compare: (a, b) => a.yuwen - b.yuwen,
multiple: 3,
},
},
{
title: '数学',
dataIndex: 'shuxue',
sorter: {
compare: (a, b) => a.shuxue - b.shuxue,
multiple: 2,
},
},
{
title: '英语',
dataIndex: 'yingyu',
sorter: {
compare: (a, b) => a.yingyu - b.yingyu,
multiple: 1,
},
},
{
title: '操作',
render: (text, record) => (
<Space>
<a onClick={() => dispatch({ type: 'deleteItem', value: record.key - 1 })}>删除</a>
</Space>
),
}
];
const rowSelection = {
selectedRowKeys,
onChange: selectedRowKeys => {},
};
let datalist = props.datalist.map((item, index) => {
item['key'] = index + 1;
return item;
})
return <Table columns={columns} dataSource={datalist} pagination={{position: ['topRight', 'none']}} rowSelection={rowSelection} />
}
import React, { Component } from 'react';
import './app.css';
import store from './redux/store';
import TableList from './components/TableList/index';
import ButtonList from './components/ButtonList/index';
import TotalList from './components/TotalList/index';
class App extends Component {
constructor(props) {
super(props);
let { datalist, selectedRowKeys } = store.getState();
this.state = { datalist, selectedRowKeys };
}
componentDidMount () {
// 需要通过 store 的订阅功能进行视图数据更新
store.subscribe(() => this.setState(store.getState()));
}
render() {
const { datalist, selectedRowKeys } = this.state;
return (
<div className="view-main">
<ButtonList datatotal={datalist.length} dispatch={store.dispatch} />
<TableList datalist={datalist} selectedRowKeys={selectedRowKeys} dispatch={store.dispatch} />
<TotalList datalist={datalist} />
</div>
);
}
}
export default App;
const defaultState = {
datalist: [
{ name: '小明', yuwen: 87, shuxue: 95, yingyu: 87 },
{ name: '小花', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '小红', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '小李', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '天天', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '菲菲', yuwen: 84, shuxue: 96, yingyu: 92 },
],
selectedRowKeys: [], // 用于判定选中项目,方便删除多列判定
};
export default (state = defaultState, action) => {
// 由于对象是引用数据类型,需要通过深拷贝赋值新对象,而此处解构的原因是为了做深拷贝
let newState = { ...state };
switch (action.type) {
case 'inputItem':
newState.datalist = newState.datalist.concat([action.value]);
break;
case 'deleteItem':
newState.datalist = newState.datalist.filter((item, index) => index !== action.value);
break;
case 'selectKeys':
newState.selectedRowKeys = action.value
break;
case 'deleteList':
newState.datalist = newState.datalist.filter(item => newState.selectedRowKeys.findIndex(index => index === item.key) === -1);
newState.selectedRowKeys = [];
break;
}
return newState;
}
在调用事务时,往往因为粗心手打错误了事务名称,在 reducer.js
仅仅是不存在于 switch
,并不会报错,这样会导致开发者花费大量的时间找问题,影响开发进度,对于开发者来说,要习惯复制粘贴,最起码的不会导致两边不统一
把 reducer.js
所有的事务抽离出来,部署到 action.js
中
export const INPUT_ITEM = 'inputItem';
export const DELETE_ITEM = 'deleteItem';
export const SELECT_KEYS = 'selectKeys';
export const DELETE_LIST = 'deleteList';
通过引入暴露的事务机制对象,在 store
或组件中引入,推荐使用复制粘贴,防止手动输入错误而导致状态管理错误,且不显现错误
import { INPUT_ITEM, DELETE_ITEM, SELECT_KEYS, DELETE_LIST } from './action';
const defaultState = {
datalist: [
{ name: '小明', yuwen: 87, shuxue: 95, yingyu: 87 },
{ name: '小花', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '小红', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '小李', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '天天', yuwen: 84, shuxue: 96, yingyu: 92 },
{ name: '菲菲', yuwen: 84, shuxue: 96, yingyu: 92 },
],
selectedRowKeys: [], // 用于判定选中项目,方便删除多列判定
};
export default (state = defaultState, action) => {
// 由于对象是引用数据类型,需要通过深拷贝赋值新对象,而此处解构的原因是为了做深拷贝
let newState = { ...state };
switch (action.type) {
case INPUT_ITEM:
newState.datalist = newState.datalist.concat([action.value]);
break;
case DELETE_ITEM:
newState.datalist = newState.datalist.filter((item, index) => index !== action.value);
break;
case SELECT_KEYS:
newState.selectedRowKeys = action.value
break;
case DELETE_LIST:
newState.datalist = newState.datalist.filter(item => newState.selectedRowKeys.findIndex(index => index === item.key) === -1);
newState.selectedRowKeys = [];
break;
}
return newState;
}
import React, { useState } from 'react';
import { Modal, Button, Form, Input } from 'antd';
import { INPUT_ITEM, DELETE_LIST } from '../../redux/action';
export default function ButtonList (props) {
const [isModalVisible, setModalVisible] = useState(false);
const [form] = Form.useForm();
const { dispatch } = props;
const showModal = () => {
setModalVisible(true);
};
const hideModal = () => {
setModalVisible(false);
};
const handleOk = () => {
form.validateFields().then(res => {
dispatch({ type: INPUT_ITEM, value: form.getFieldsValue() });
setModalVisible(false);
});
}
return (
<>
<div style={{ position: 'absolute', top: '16px', zIndex: 1 }}>
<Button type="primary" danger style={{marginRight: '20px'}} onClick={() => dispatch({ type: DELETE_LIST })}>全部删除</Button>
<Button type="primary" onClick={showModal}>新增</Button>
</div>
<Modal title="Basic Modal" visible={isModalVisible} onOk={handleOk} onCancel={hideModal}>
<Form layout={{ labelCol: { span: 8 }, wrapperCol: { span: 16 } }} form={form} name="control-hooks">
<Form.Item name="name" label="姓名" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="yuwen" label="语文" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="shuxue" label="数学" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="yingyu" label="英语" rules={[ { required: true } ]} >
<Input />
</Form.Item>
</Form>
</Modal>
</>
)
}
import React from 'react';
import { Table, Space } from 'antd';
import { DELETE_ITEM, SELECT_KEYS } from '../../redux/action';
export default function TableList (props) {
const { selectedRowKeys, dispatch } = props;
const columns = [
{
title: '姓名',
dataIndex: 'name',
},
{
title: '语文',
dataIndex: 'yuwen',
sorter: {
compare: (a, b) => a.yuwen - b.yuwen,
multiple: 3,
},
},
{
title: '数学',
dataIndex: 'shuxue',
sorter: {
compare: (a, b) => a.shuxue - b.shuxue,
multiple: 2,
},
},
{
title: '英语',
dataIndex: 'yingyu',
sorter: {
compare: (a, b) => a.yingyu - b.yingyu,
multiple: 1,
},
},
{
title: '操作',
render: (text, record) => (
<Space>
<a onClick={() => dispatch({ type: DELETE_ITEM, value: record.key - 1 })}>删除</a>
</Space>
),
}
];
const rowSelection = {
selectedRowKeys,
onChange: selectedRowKeys => dispatch({ type: SELECT_KEYS, value: selectedRowKeys }),
};
let datalist = props.datalist.map((item, index) => {
item['key'] = index + 1;
return item;
})
return <Table columns={columns} dataSource={datalist} pagination={{position: ['topRight', 'none']}} rowSelection={rowSelection} />
}
react 状态管理
react-redux的两个最主要功能:
- Provider:提供包含store的context
- connect:连接容器组件和傻瓜组件;
$ npm install --save react-redux
provider 提供者
将用户状态管理 store
绑定在 index.js
上,这样内部所有的子组件都能通过 connect
获取
import React from 'react';
import ReactDOM from 'react-dom';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/es/locale/zh_CN'; // 引入中文包
import { Provider } from 'react-redux';
import store from './redux/store';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<ConfigProvider locale={zhCN}>
<Provider store={store}>
<App />
</Provider>
</ConfigProvider>
</React.StrictMode>,
document.getElementById('root')
);
import React from 'react';
import './app.css';
import TableList from './components/TableList/index';
import ButtonList from './components/ButtonList/index';
import TotalList from './components/TotalList/index';
function App () {
return (
<div className="view-main">
<ButtonList />
<TableList />
<TotalList />
</div>
);
}
export default App;
connect 接收两个参数 mapStateToProps 和 mapDispatchToProps,第一次是 connect
函数的执行,第二次是把 connect
函数返回的函数再次执行,最后产生的就是容器组件
- 把 Store 上的状态转化为内层傻瓜组件的 prop
- 把内层傻瓜组件中的用户动作转化为派送给 Store 的动作
1)首先渲染列表 TableList
import React from 'react';
import { connect } from 'react-redux';
import { Table, Space } from 'antd';
import { DELETE_ITEM, SELECT_KEYS } from '../../redux/action';
function TableList (props) {
const { selectedRowKeys, datalist } = props;
const columns = [
{
title: '姓名',
dataIndex: 'name',
},
{
title: '语文',
dataIndex: 'yuwen',
sorter: {
compare: (a, b) => a.yuwen - b.yuwen,
multiple: 3,
},
},
{
title: '数学',
dataIndex: 'shuxue',
sorter: {
compare: (a, b) => a.shuxue - b.shuxue,
multiple: 2,
},
},
{
title: '英语',
dataIndex: 'yingyu',
sorter: {
compare: (a, b) => a.yingyu - b.yingyu,
multiple: 1,
},
},
{
title: '操作',
render: (text, record) => (
<Space>
<a onClick={() => props.deleteItem(record.key)}>删除</a>
</Space>
),
}
];
const rowSelection = {
selectedRowKeys,
onChange: selectedRowKeys => props.selectKeys(selectedRowKeys),
};
let dataSource = datalist.map((item, index) => {
item['key'] = index;
return item;
});
return (
<Table
columns={columns} dataSource={dataSource}
pagination={{position: ['topRight', 'none']}}
rowSelection={rowSelection}
/>
)
}
// 用于接收状态数据,并将状态数据传递给组件
function mapStateToProps (state) {
return {
datalist: state.datalist,
selectedRowKeys: state.selectedRowKeys
}
}
// 用于接收状态变更,并将状态变更传递给组件
function mapDispatchToProps (dispatch) {
return {
deleteItem: value => dispatch({ type: DELETE_ITEM, value }),
selectKeys: value => dispatch({ type: SELECT_KEYS, value })
}
}
// connect 接收的 mapStateToProps, mapDispatchToProps 方法返回的结果后成为一个高阶组件,传递给 TableList 组件
export default connect(mapStateToProps, mapDispatchToProps)(TableList)
2)其次渲染头部 ButtonList
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { Modal, Button, Form, Input } from 'antd';
import { INPUT_ITEM, DELETE_LIST } from '../../redux/action';
function ButtonList (props) {
const [isModalVisible, setModalVisible] = useState(false);
const [form] = Form.useForm();
const showModal = () => {
setModalVisible(true);
};
const hideModal = () => {
setModalVisible(false);
};
const handleOk = () => {
form.validateFields().then(res => {
props.inputItem(form.getFieldsValue());
setModalVisible(false);
});
}
return (
<>
<div style={{ position: 'absolute', top: '16px', zIndex: 1 }}>
<Button type="primary" danger style={{marginRight: '20px'}} onClick={() => props.deleteList()}>全部删除</Button>
<Button type="primary" onClick={showModal}>新增</Button>
</div>
<Modal title="Basic Modal" visible={isModalVisible} onOk={handleOk} onCancel={hideModal}>
<Form layout={{ labelCol: { span: 8 }, wrapperCol: { span: 16 } }} form={form} name="control-hooks">
<Form.Item name="name" label="姓名" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="yuwen" label="语文" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="shuxue" label="数学" rules={[ { required: true } ]} >
<Input />
</Form.Item>
<Form.Item name="yingyu" label="英语" rules={[ { required: true } ]} >
<Input />
</Form.Item>
</Form>
</Modal>
</>
)
}
// 用于接收状态数据,并将状态数据传递给组件
function mapStateToProps (state) {
return {
datalist: state.datalist,
selectedRowKeys: state.selectedRowKeys
}
}
// 用于接收状态变更,并将状态变更传递给组件
function mapDispatchToProps (dispatch) {
return {
inputItem: value => dispatch({ type: INPUT_ITEM, value }),
deleteList: () => dispatch({ type: DELETE_LIST })
}
}
// connect 接收的 mapStateToProps, mapDispatchToProps 方法返回的结果后成为一个高阶组件,传递给 ButtonList 组件
export default connect(mapStateToProps, mapDispatchToProps)(ButtonList)
3)最后渲染底部 TotalList
import { Row, Col } from 'antd';
import { connect } from 'react-redux';
function TotalList (props) {
const { datalist } = props, len = datalist.length;
let totalyw = 0, totalsx = 0, totalyy = 0;
datalist.map(item => {
totalyw += item.yuwen;
totalsx += item.shuxue;
totalyy += item.yingyu;
})
return (
<Row style={{lineHeight: '40px', borderBottom: '1px solid #cccccc'}}>
<Col span={4} style={{marginRight: '26px', paddingLeft: '20px'}}>平均分</Col>
<Col span={4} style={{marginRight: '34px'}}>{totalyw / len}</Col>
<Col span={4} style={{marginRight: '36px'}}>{totalsx / len}</Col>
<Col span={4}>{totalyy / len}</Col>
<Col span={4}></Col>
</Row>
)
}
// 用于接收状态数据,并将状态数据传递给组件
function mapStateToProps (state) {
return {
datalist: state.datalist,
selectedRowKeys: state.selectedRowKeys
}
}
// connect 接收的 mapStateToProps, mapDispatchToProps 方法返回的结果后成为一个高阶组件,传递给 TotalList 组件
// 由于底部暂未存在状态更新,就可以返回一个空对象即可
export default connect(mapStateToProps, (dispatch) => ({}))(TotalList)
react 两者区别
redux 是 react 中进行 state 状态管理的 JS 库(并不是 react 插件),一般是管理多个组件中共享数据状态
react-redux 是 redux 的官方 react 绑定库。它能够使你的 react 组件从 redux store 中读取数据,并且向 store 分发 actions 以更新数据
- redux 和组件进行对接的时候是直接在组件中进行创建,react-redux 是运用 Provider 将组件和 store 对接
- redux 获取 state 是直接通过 store.getState(),react-redux 获取 state 是通过 mapStateToProps 函数,只要 state 数据变化就能获取最新数据
- redux 是使用 dispatch 直接触发,来操作 store 的数据,react-redux 是使用 mapDispathToProps 函数然后在调用 dispatch 进行触发
React 组件传值
- 父子组件传值:父传子,通过 props 属性主动传播,子传父,通过调用父传子方法回调被动传播
- 状态提升传值:两个非父子组件之间的传值,通过设定一个共同的父组件,采用通用的父组件 state
- 数据共享传值:通过创建一个 context【上下链接】对象,基于 Provider【供应者】—— Consumer【消费者】来进行数据的获取
- 路由对象传值:基于 history 监控路由和设置路由,match、location 得到路由参数
- 数据状态管理:基于 redux 对业务数据以管理方式进行数据的增、删、改、查处理