- Redux学习
- 1.用react脚手架创建一个新的项目
- 2.visual studio code安装simple react snippet 插件
- 3.然后再src目录下新建一个文件,TodoList.jsx
- 4.安装antd组件制作UI界面
- 5.使用antd制作UI界面
- 6.安装redux
- 7.创建redux中的仓库-store和reducer
- 8.如何将TodoList里面的功能用redux实现呢?
- 9.通过input体验Redux的流程
- 10.通过TodoList列表体验Redux的流程
- 11.用Redux实现TodoList的删除功能
- 12.Redux的技巧
- 13.Redux常遇到的坑
- 14.组件UI和业务逻辑的拆分
- 15.Redux中的无状态组件
- 16.Axios异步获取数据并和Redux结合
- 17.Redux-thunk中间件的安装和配置
- 18.Redux-saga中间件的安装和配置
- 19.React-Redux的介绍和安装
Redux学习
redux 的工作流是单向数据流
react 组件相当于借书者
action相当于图书管理员
store相当于图书馆
reducers是图书管理软件
react-action-store-reducers-store-react
步骤
1.用react脚手架创建一个新的项目
2.visual studio code安装simple react snippet 插件
3.然后再src目录下新建一个文件,TodoList.jsx
在使用到snippet的地方 , 用快捷键ccc
来生成react代码片段
4.安装antd组件制作UI界面
npm install antd --save-dev
5.使用antd制作UI界面
编辑文件(TodoList.jsx),如下代码,这样,基本的列表框就已经实现了(TodoList.jsx)
import React, { Component } from "react";
import 'antd/dist/antd.css';
import { Input, Button, List } from "antd";
const data = [
'早8点开晨会',
'早10点开晨会1',
'早12点开晨会2'
]
class TodoList extends Component {
render() {
return (
<div>
<div style={{ margin: '2rem' }}>
<Input placeholder="write something" style={{ '250px', marginRight: '10px' }}></Input>
<Button type="primary">增加</Button>
</div>
<div style={{ margin: '10px', '300px' }}>
<List
bordered
dataSource={data}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
</div>
);
}
}
export default TodoList;
6.安装redux
npm install redux --save-dev
7.创建redux中的仓库-store和reducer
在src目录新建文件夹store,在store目录下面文件新建index.js文件,代码如下,该文件用于新建仓库
import { createStore } from 'redux'
//建立仓库
const store = createStore();
export default store;
在store目录下新建reducer.js文件,该文件类似管理员的角色,代码如下
const defaultState = {};
export default (state = defaultState, action) => {
return state;
}
最后修改index.js文件,将reducer引入到仓库里面,index.js文件代码如下,这样我们就将reducer和仓库建立了联系。
import { createStore } from 'redux'
import reducer from './reducer'
//建立仓库
const store = createStore(reducer);
export default store;
8.如何将TodoList里面的功能用redux实现呢?
首先将初始值存储到reducer.js文件,代码如下:
const defaultState = {
inputValue: 'Write something',
list: [
'早8点开晨会',
'早10点开晨会1',
'早12点开晨会2'
]
};
export default (state = defaultState, action) => {
return state;
}
然后在TodoList.js文件里面删除初始值,引入store,如下代码,刷新页面发现也可以正常显示了
import React, { Component } from "react";
import 'antd/dist/antd.css';
import { Input, Button, List } from "antd";
import store from './store/'
class TodoList extends Component {
constructor(props) {
super(props);
console.log(store.getState())
this.state=store.getState();
}
render() {
return (
<div>
<div style={{ margin: '2rem' }}>
<Input placeholder={this.state.inputValue}
style={{ '250px', marginRight: '10px' }}></Input>
<Button type="primary">增加</Button>
</div>
<div style={{ margin: '10px', '300px' }}>
<List
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
</div>
);
}
}
export default TodoList;
安装google浏览器插件Redux DevTools,但是发现redux工具还使用不了
去网页上可以https://github.com/zalmoxisus/redux-devtools-extension#usage 可以看到使用redux的基本过程,修改store/index.js代码,代码如下所示:
import { createStore } from 'redux'
import reducer from './reducer'
//建立仓库
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
这样就可以在浏览器中使用redux dev tools工具了。
9.通过input体验Redux的流程
通过给input 添加动态变化事件来体验redux的过程,如下代码,给input添加onChange事件,然后通过dipatch将action通知到reducer那边,TodoList.js代码如下。
import React, { Component } from "react";
import 'antd/dist/antd.css';
import { Input, Button, List } from "antd";
import store from './store/'
class TodoList extends Component {
constructor(props) {
super(props);
console.log(store.getState())
this.state = store.getState();
this.changeInputValue = this.changeInputValue.bind(this);
}
changeInputValue(e) {
console.log(e.target.value);
const action = {
type: 'changeInput',
value: e.target.value
}
store.dispatch(action);
}
render() {
return (
<div>
<div style={{ margin: '2rem' }}>
<Input placeholder={this.state.inputValue}
style={{ '250px', marginRight: '10px' }}
onChange={this.changeInputValue}
></Input>
<Button type="primary">增加</Button>
</div>
<div style={{ margin: '10px', '300px' }}>
<List
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
</div>
);
}
}
export default TodoList;
在reducer.js文件中打印出action,就会发现action的内容如下,
const defaultState = {
inputValue: 'Write something',
list: [
'早8点开晨会',
'早10点开晨会1',
'早12点开晨会2'
]
};
export default (state = defaultState, action) => {
console.log(action);
//{type: "changeInput", value: "ggggfffffffffffffffffff"}
//reducer里面只能接受state,不能改变state
return state;
}
当每次改变文本框的值时,就会通知到reducer这边,action的内容就会被传递到这里。
reducer里面只能接受state,不能改变state,如果想要修改state里面内容呢,index.js代码如下:
刷新页面,可以看到state里面的变量值已经发生了变化。
const defaultState = {
inputValue: 'Write something',
list: [
'早8点开晨会',
'早10点开晨会1',
'早12点开晨会2'
]
};
export default (state = defaultState, action) => {
console.log(state, action);
//reducer里面只能接受state,不能改变state
if (action.type == 'changeInput') {
//
let newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
return state;
}
当时这里有个问题就是当设置了input的value值,必须要有一个redux的订阅动作,state里面的变量才会刷新,如果没有这个订阅,则不会刷新,TodoList.js代码如下所示。
import React, { Component } from "react";
import 'antd/dist/antd.css';
import { Input, Button, List } from "antd";
import store from './store/'
class TodoList extends Component {
constructor(props) {
super(props);
console.log(store.getState())
this.state = store.getState();
this.changeInputValue = this.changeInputValue.bind(this);
this.storeChange = this.storeChange.bind(this);
}
changeInputValue(e) {
console.log(e.target.value);
const action = {
type: 'changeInput',
value: e.target.value
}
store.dispatch(action);
store.subscribe(this.storeChange);
}
storeChange() {
this.setState(store.getState())
}
render() {
return (
<div>
<div style={{ margin: '2rem' }}>
<Input placeholder={this.state.inputValue}
style={{ '250px', marginRight: '10px' }}
onChange={this.changeInputValue}
value={this.state.inputValue}
></Input>
<Button type="primary">增加</Button>
</div>
<div style={{ margin: '10px', '300px' }}>
<List
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
</div>
);
}
}
export default TodoList;
10.通过TodoList列表体验Redux的流程
给按钮添加功能函数,当增加按钮被点击时,下面的list组件就会发生变化,
TodoList.js文件代码如下所示:通过store.dispatch(action) 函数发送给reducers。
import React, { Component } from "react";
import 'antd/dist/antd.css';
import { Input, Button, List } from "antd";
import store from './store/'
class TodoList extends Component {
constructor(props) {
super(props);
console.log(store.getState())
this.state = store.getState();
this.changeInputValue = this.changeInputValue.bind(this);
this.storeChange = this.storeChange.bind(this);
this.clickBtn = this.clickBtn.bind(this);
}
changeInputValue(e) {
console.log(e.target.value);
const action = {
type: 'changeInput',
value: e.target.value
}
store.dispatch(action);
store.subscribe(this.storeChange);
}
storeChange() {
this.setState(store.getState())
}
clickBtn() {
const action = { type: 'addItem' }
store.dispatch(action);
}
render() {
return (
<div>
<div style={{ margin: '2rem' }}>
<Input placeholder={this.state.inputValue}
style={{ '250px', marginRight: '10px' }}
onChange={this.changeInputValue}
value={this.state.inputValue}
></Input>
<Button type="primary" onClick={this.clickBtn}>增加</Button>
</div>
<div style={{ margin: '10px', '300px' }}>
<List
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
</div>
);
}
}
export default TodoList;
相应地,Reducer.js 文件内容也要添加这个action相关的内容 ,当识别到增加按钮发来的action时,将state状态更新。代码如下所示:
const defaultState = {
inputValue: 'Write something',
list: [
'早8点开晨会',
'早10点开晨会1',
'早12点开晨会2'
]
};
export default (state = defaultState, action) => {
console.log(state, action);
//reducer里面只能接受state,不能改变state
if (action.type === 'changeInput') {
//
let newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === 'addItem') {
let newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = '';
return newState;
}
return state;
}
当点击添加按钮之后,input文本框的内容置空,列表框也会增加内容了。这是因为TodoList.js文件中,已经订阅过相关内容,store.subscribe(this.storeChange)
当store里面的state内容 发生变化时,界面也会相应发生变化。如果将订阅的代码去掉,则界面不会发生变化。
11.用Redux实现TodoList的删除功能
如下代码,实现删除功能,TodoList.js代码如下所示:
import React, { Component } from "react";
import 'antd/dist/antd.css';
import { Input, Button, List } from "antd";
import store from './store/'
class TodoList extends Component {
constructor(props) {
super(props);
console.log(store.getState())
this.state = store.getState();
this.changeInputValue = this.changeInputValue.bind(this);
this.storeChange = this.storeChange.bind(this);
this.clickBtn = this.clickBtn.bind(this);
store.subscribe(this.storeChange);
}
changeInputValue(e) {
console.log(e.target.value);
const action = {
type: 'changeInput',
value: e.target.value
}
store.dispatch(action);
}
storeChange() {
this.setState(store.getState())
}
clickBtn() {
const action = { type: 'addItem' }
store.dispatch(action);
}
deleteItem(index) {
const action = {
type: 'deleteItem',
index
};
console.log("index is" + index);
store.dispatch(action);
}
render() {
return (
<div>
<div style={{ margin: '2rem' }}>
<Input placeholder={this.state.inputValue}
style={{ '250px', marginRight: '10px' }}
onChange={this.changeInputValue}
value={this.state.inputValue}
></Input>
<Button type="primary" onClick={this.clickBtn}>增加</Button>
<Button type="primary" onClick={this.deleteItem}>删除</Button>
</div>
<div style={{ margin: '10px', '300px' }}>
<List
bordered
dataSource={this.state.list}
renderItem={(item, index) => <List.Item onClick={this.deleteItem.bind(this, index)}>{item}</List.Item>}
/>
</div>
</div>
);
}
}
export default TodoList;
相应的,reducer.js里面也要添加相应的内容,如下代码
const defaultState = {
inputValue: 'Write something',
list: [
'早8点开晨会',
'早10点开晨会1',
'早12点开晨会2'
]
};
export default (state = defaultState, action) => {
console.log(state);
console.log(action);
//reducer里面只能接受state,不能改变state
if (action.type === 'changeInput') {
//
let newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === 'addItem') {
let newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = '';
return newState;
}
if(action.type==='deleteItem') {
let newState = JSON.parse(JSON.stringify(state));
console.log(newState);
newState.list.splice(action.index,1);
return newState;
}
return state;
}
12.Redux的技巧
看 上面的例子可以发现每次给reducer里面处理action的类型,如果写错了类型会很麻烦,不好定位,而且代码看起来也很乱。如何处理呢?下面我们通过在store目录下新建actionTypes.js文件,将action相关的内容用常量来表示,注意常量要大写哦。actionTypes.js代码如下:
export const CHANGE_INPUT = 'changeInput'
export const ADD_ITEM = 'addItem'
export const DELETE_ITEM = 'deleteItem'
接着我们将TodoList.js文件引入actionTypes.js,文件中涉及到的相应字符串进行替换为常量。
reducer.js文件也是类似的。
还有一个问题就是可以整理一下action相关的内容,在store目录下面新建actionCreatore.js文件,代码如下:
import { CHANGE_INPUT, ADD_ITEM, DELETE_ITEM } from './actionTypes';
export const changeInputAction = (value) => ({
type: CHANGE_INPUT,
value
})
export const addItemAction = () => ({
type: ADD_ITEM
})
export const deleteItemAction = (index) => ({
type: DELETE_ITEM,
index
})
修改TodoList.js文件内容,使用actionCreators封装好的方法,代码如下:
import React, { Component } from "react";
import 'antd/dist/antd.css';
import { Input, Button, List } from "antd";
import store from './store/'
import { changeInputAction, addItemAction, deleteItemAction } from './store/actionCreators'
class TodoList extends Component {
constructor(props) {
super(props);
console.log(store.getState())
this.state = store.getState();
this.changeInputValue = this.changeInputValue.bind(this);
this.storeChange = this.storeChange.bind(this);
this.clickBtn = this.clickBtn.bind(this);
store.subscribe(this.storeChange);
}
changeInputValue(e) {
console.log(e.target.value);
const action = changeInputAction(e.target.value);
store.dispatch(action);
}
storeChange() {
this.setState(store.getState())
}
clickBtn() {
const action = addItemAction();
store.dispatch(action);
}
deleteItem(index) {
const action = deleteItemAction(index);
console.log("index is" + index);
store.dispatch(action);
}
render() {
return (
<div>
<div style={{ margin: '2rem' }}>
<Input placeholder={this.state.inputValue}
style={{ '250px', marginRight: '10px' }}
onChange={this.changeInputValue}
value={this.state.inputValue}
></Input>
<Button type="primary" onClick={this.clickBtn}>增加</Button>
</div>
<div style={{ margin: '10px', '300px' }}>
<List
bordered
dataSource={this.state.list}
renderItem={(item, index) => <List.Item onClick={this.deleteItem.bind(this, index)}>{item}</List.Item>}
/>
</div>
</div>
);
}
}
export default TodoList;
13.Redux常遇到的坑
1.store唯一性:多个store是坚决不允许,只能有一个store空间
2.只有store能改变自己的内容,reducer不能改变
3.reducer必须是纯函数*,纯函数,函数返回的结果由参数输入值 决定。
reducer里面不能调用ajax请求,因为服务器传递回来的数据可能不是固定的。
14.组件UI和业务逻辑的拆分
上面的代码里面,UI和业务逻辑其实还是融合在一起的。怎么解耦呢?
1.在src目录下面,新建文件TodoListUI.js,专门放组件的UI部分,利用快速生成的方式imrc生成import,将TodoList里面的render函数直接剪切到TodoListUI.js文件里面,如下代码。
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from "antd";
class TodoListUI extends Component {
render() {
return (
<div>
<div style={{ margin: '2rem' }}>
<Input placeholder={this.state.inputValue}
style={{ '250px', marginRight: '10px' }}
onChange={this.changeInputValue}
value={this.state.inputValue}
></Input>
<Button type="primary" onClick={this.clickBtn}>增加</Button>
</div>
<div style={{ margin: '10px', '300px' }}>
<List
bordered
dataSource={this.state.list}
renderItem={(item, index) => <List.Item onClick={this.deleteItem.bind(this, index)}>{item}</List.Item>}
/>
</div>
</div>
);
}
}
export default TodoListUI;
2.在TodoList.js的render函数中引入TodoListUI组件,代码如下:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from "antd";
class TodoListUI extends Component {
render() {
console.log(this.props);
return (
<div>
<div style={{ margin: '2rem' }}>
<Input placeholder={this.props.inputValue}
style={{ '250px', marginRight: '10px' }}
onChange={this.props.changeInputValue}
value={this.props.inputValue}
></Input>
<Button type="primary" onClick={this.props.clickBtn}>增加</Button>
</div>
<div style={{ margin: '10px', '300px' }}>
<List
bordered
dataSource={this.props.list}
renderItem={(item, index) =>
<List.Item onClick={(index) => { this.props.deleteItem(index) }}>{item}</List.Item>
}
/>
</div>
</div>
);
}
}
export default TodoListUI;
TodoList.js组件代码如下:
import React, { Component } from "react";
import 'antd/dist/antd.css';
import store from './store/'
import { changeInputAction, addItemAction, deleteItemAction } from './store/actionCreators'
import TodoListUI from './TodoListUI';
class TodoList extends Component {
constructor(props) {
super(props);
console.log(store.getState())
this.state = store.getState();
this.changeInputValue = this.changeInputValue.bind(this);
this.storeChange = this.storeChange.bind(this);
this.clickBtn = this.clickBtn.bind(this);
store.subscribe(this.storeChange);
this.deleteItem = this.deleteItem.bind(this);
}
changeInputValue(e) {
console.log(e.target.value);
const action = changeInputAction(e.target.value);
store.dispatch(action);
}
storeChange() {
this.setState(store.getState())
}
clickBtn() {
const action = addItemAction();
store.dispatch(action);
}
deleteItem(index) {
const action = deleteItemAction(index);
console.log("index is" + index);
store.dispatch(action);
}
render() {
return (
<TodoListUI
inputValue={this.state.inputValue}
changeInputValue={this.changeInputValue}
clickBtn={this.clickBtn}
list={this.state.list}
deleteItem={this.deleteItem}
/>
);
}
}
export default TodoList;
3.运行上述的代码,会发现功能会实现,但是删除的时候删错了,是因为List.Item里面的点击函数,没有使用到renderItem传入的index,而是自己定义的index,其实没有用到renderItem传入的index。
<List
bordered
dataSource={this.props.list}
renderItem={(item, index) =>
<List.Item onClick={(index) => { this.props.deleteItem(index) }}>{item}</List.Item>
修改成下面的代码,功能就正常了。
<List
bordered
dataSource={this.props.list}
renderItem={(item, index) =>
<List.Item onClick={() => { this.props.deleteItem(index) }}>{item}</List.Item>
}
/>
15.Redux中的无状态组件
将TodoListUI.js改造成无状态组件,无状态组件就是一个函数了,不用继承component,使用jsx必须引入React。无状态组件的性能要比有状态组件高一些,代码如下:
import React from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from "antd";
// class TodoListUI extends Component {
// render() {
// console.log(props);
// return (
// <div>
// <div style={{ margin: '2rem' }}>
// <Input placeholder={props.inputValue}
// style={{ '250px', marginRight: '10px' }}
// onChange={props.changeInputValue}
// value={props.inputValue}
// ></Input>
// <Button type="primary" onClick={props.clickBtn}>增加</Button>
// </div>
// <div style={{ margin: '10px', '300px' }}>
// <List
// bordered
// dataSource={props.list}
// renderItem={(item, index) =>
// <List.Item onClick={() => { props.deleteItem(index) }}>{item}</List.Item>
// }
// />
// </div>
// </div>
// );
// }
// }
const TodoListUI = (props) => {
return (
<div>
<div style={{ margin: '2rem' }}>
<Input placeholder={props.inputValue}
style={{ '250px', marginRight: '10px' }}
onChange={props.changeInputValue}
value={props.inputValue}
></Input>
<Button type="primary" onClick={props.clickBtn}>增加</Button>
</div>
<div style={{ margin: '10px', '300px' }}>
<List
bordered
dataSource={props.list}
renderItem={(item, index) =>
<List.Item onClick={() => { props.deleteItem(index) }}>{item}</List.Item>
}
/>
</div>
</div>
);
}
export default TodoListUI;
16.Axios异步获取数据并和Redux结合
先安装Axios,使用下面的命令
npm install axios --save-dev
在TodoList.js里面引入axios,然后在生命周期函数里面添加axios的相关东西,从axios里面获取数据,然后调用reducer里面的函数,给state中的list赋值 。
17.Redux-thunk中间件的安装和配置
redex-thunk中间件,常将调用接口的操作放到redux-thunk中去。
安装
npm install redux-thunk --save-dev
接下来是配置redux-thunk
首先是在store/index.js中引入,有个问题是redux-thunk引入的时候,之前的redux-dev插件就不能使用了,为了解决这个问题,引入增强函数,修改store/index.js的内容,代码如下:
import { createStore, applyMiddleware, compose } from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(applyMiddleware(thunk))
//建立仓库
const store = createStore(reducer, enhancer);
export default store;
18.Redux-saga中间件的安装和配置
npm install react-saga --save-dev
Redux-saga和react-thunk类似。这里先不详细介绍了
19.React-Redux的介绍和安装
安装react-redux插件,可以简化redux流程。
新建一个脚手架项目,只保留index.js文件,
create-react-app react-redux-project
安装react-redux插件
npm install react-redux --save-dev
安装redux插件
npm install redux --save-dev
在src目录下,新建TodoList.js文件,代码如下:
import React, { Component } from 'react';
import store from './store'
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState();
}
render() {
return (
<div>
<div>
<input value={this.state.inputValue}></input>
<button>提交</button>
</div>
<ul>
<li>fff</li>
</ul>
</div>
);
}
}
export default TodoList;
在src新建store目录,在store目录下新建文件index.js文件和reducer.js文件,index.js文件代码如下:
import { createStore } from 'redux'
import reducer from './reducers'
const store = createStore(reducer)
export default store;
reducers.js文件代码如下:
const defaultState = {
inputValue: 'jjj',
list: []
}
export default (state = defaultState, action) => {
return state;
}
刷新页面,此时页面可以成功地获取到state里面的内容了。
接下来是react-redux插件的内容了。
19.1react-redux的Provider和connect
修改src目录下的index.js文件内容,只要被Provider包围的组件,都可以获得store里面的内容,代码如下,通过将store用Provider包围TodoList组件,TodoList组件就可以获得store里面的内容了。这样如果其他组件也想用到store的内容,直接放到Provider里面就可以了。
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
import { Provider } from 'react-redux';
import store from './store'
const App = (
<Provider store={store}>
<TodoList />
</Provider>
)
ReactDOM.render(App,
document.getElementById('root')
);
将TodoList组件用Provider包裹起来后,要在TodoList.js文件里面使用connect连接这个Provider中的内容,修改代码如下,通过将state变量映射成为props属性,在TodoList.js文件中(通过 this.props.inputValue)可以获取store里面存储的值。
import React, { Component } from 'react';
import { connect } from 'react-redux'
class TodoList extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div>
<input value={this.props.inputValue}></input>
<button>提交</button>
</div>
<ul>
<li>fff</li>
</ul>
</div>
);
}
}
const stateToProps = (state) => {
console.log(state);
//{inputValue: "jjj", list: Array(0)}
return {
inputValue: state.inputValue
}
}
// export default TodoList;
export default connect(stateToProps, null)(TodoList);
19.2 修改store和state
首先给文本框添加onChange函数,TodoList.js代码如下:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class TodoList extends Component {
constructor(props) {
super(props);
}
changeValue(e) {
console.log(e.target.value);
}
render() {
return (
<div>
<div>
<input
value={this.props.inputValue}
onChange={this.changeValue.bind(this)}></input>
<button>提交</button>
</div>
<ul>
<li>fff</li>
</ul>
</div>
);
}
}
const stateToProps = (state) => {
console.log(state);
//{inputValue: "jjj", list: Array(0)}
return {
inputValue: state.inputValue
}
}
// export default TodoList;
export default connect(stateToProps, null)(TodoList);
接下来通过连接器将onChange的内容传递到store里面,修改store中的状态值。将changeValue方法放到dispatchToProps方法中,文本框的绑定事件也可以用this.props.changeValue访问了,代码如下:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class TodoList extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div>
<input
value={this.props.inputValue}
onChange={this.props.changeValue}></input>
<button>提交</button>
</div>
<ul>
<li>fff</li>
</ul>
</div>
);
}
}
const dispatchToProps = (dispatch) => {
return {
changeValue(e) {
console.log(e.target.value);
}
}
}
const stateToProps = (state) => {
console.log(state);
//{inputValue: "jjj", list: Array(0)}
return {
inputValue: state.inputValue
}
}
// export default TodoList;
export default connect(stateToProps, dispatchToProps)(TodoList);
一切正常了,最后我们需要将派发的函数修改一下,传到reducer中去,和之前一样。TodoList.js代码如下:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class TodoList extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div>
<input
value={this.props.inputValue}
onChange={this.props.changeValue}></input>
<button>提交</button>
</div>
<ul>
<li>fff</li>
</ul>
</div>
);
}
}
const dispatchToProps = (dispatch) => {
return {
changeValue(e) {
console.log(e.target.value);
let action = {
type: 'Change_input',
value: e.target.value
}
//通过回调函数中的dispatch可以访问到store中的dispatch
dispatch(action);
}
}
}
const stateToProps = (state) => {
console.log(state);
//{inputValue: "jjj", list: Array(0)}
return {
inputValue: state.inputValue
}
}
// export default TodoList;
export default connect(stateToProps, dispatchToProps)(TodoList);
reducer.js文件代码如下:
const defaultState = {
inputValue: 'jjj',
list: []
}
export default (state = defaultState, action) => {
if (action.type === 'Change_input') {
let newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
return state;
}
这样文本框就可以输入值并且发生变化了。
19.4 增加和删除List数据
接下来是增加和删除List中的数据,TodoList.js文件代码如下:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class TodoList extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div>
<input
value={this.props.inputValue}
onChange={this.props.changeValue}></input>
<button onClick={this.props.clickButton}>提交</button>
</div>
<ul>
{
this.props.list.map((value, index) => {
return (<li
key={index}
onClick={()=>{this.props.clickDelete(index)}}>{value}</li>)
})
}
</ul>
</div>
);
}
}
const dispatchToProps = (dispatch) => {
return {
changeValue(e) {
console.log(e.target.value);
let action = {
type: 'Change_input',
value: e.target.value
}
dispatch(action);
},
clickButton() {
let action = {
type: 'Add_item'
}
dispatch(action);
},
clickDelete(index) {
console.log(index);
let action = {
type: 'Delete_item',
index
}
dispatch(action);
}
}
}
const stateToProps = (state) => {
console.log(state);
//{inputValue: "jjj", list: Array(0)}
return {
inputValue: state.inputValue,
list: state.list
}
}
// export default TodoList;
export default connect(stateToProps, dispatchToProps)(TodoList);
reducer.js代码如下:
const defaultState = {
inputValue: 'jjj',
list: []
}
export default (state = defaultState, action) => {
if (action.type === 'Change_input') {
let newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === 'Add_item') {
let newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = '';
return newState;
}
if (action.type === 'Delete_item') {
let newState = JSON.parse(JSON.stringify(state));
newState.list.splice(newState.index,1);
return newState;
}
return state;
}
19.5代码优化
解构赋值和无状态组件(只有render函数的组件可以优化为无状态组件),TodoList.js代码优化如下。
import React from 'react';
import { connect } from 'react-redux'
const TodoList = (props) => {
let { inputValue, changeValue, clickButton, list, clickDelete } = props;
return (
<div>
<div>
<input
value={inputValue}
onChange={changeValue}></input>
<button onClick={clickButton}>提交</button>
</div>
<ul>
{
list.map((value, index) => {
return (<li
key={index}
onClick={() => { clickDelete(index) }}>{value}</li>)
})
}
</ul>
</div>
);
}
// class TodoList extends Component {
// render() {
// let {inputValue,changeValue,clickButton,list,clickDelete}=this.props;
// return (
// <div>
// <div>
// <input
// value={inputValue}
// onChange={changeValue}></input>
// <button onClick={clickButton}>提交</button>
// </div>
// <ul>
// {
// list.map((value, index) => {
// return (<li
// key={index}
// onClick={()=>{clickDelete(index)}}>{value}</li>)
// })
// }
// </ul>
// </div>
// );
// }
// }
const dispatchToProps = (dispatch) => {
return {
changeValue(e) {
console.log(e.target.value);
let action = {
type: 'Change_input',
value: e.target.value
}
dispatch(action);
},
clickButton() {
let action = {
type: 'Add_item'
}
dispatch(action);
},
clickDelete(index) {
console.log(index);
let action = {
type: 'Delete_item',
index
}
dispatch(action);
}
}
}
const stateToProps = (state) => {
console.log(state);
//{inputValue: "jjj", list: Array(0)}
return {
inputValue: state.inputValue,
list: state.list
}
}
// export default TodoList;
export default connect(stateToProps, dispatchToProps)(TodoList);
通过上面的代码,可以发现通过react-redux,可以很容易将一个状态组件变成一个无状态组件,提高效率。大型项目可以通过stateToProps和dispatchToProps拆分成n个文件,方便多个人进行开发。