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={{ width: '250px', marginRight: '10px' }}></Input>
<Button type="primary">增加</Button>
</div>
<div style={{ margin: '10px', width: '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={{ width: '250px', marginRight: '10px' }}></Input>
<Button type="primary">增加</Button>
</div>
<div style={{ margin: '10px', width: '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={{ width: '250px', marginRight: '10px' }}
onChange={this.changeInputValue}
></Input>
<Button type="primary">增加</Button>
</div>
<div style={{ margin: '10px', width: '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={{ width: '250px', marginRight: '10px' }}
onChange={this.changeInputValue}
value={this.state.inputValue}
></Input>
<Button type="primary">增加</Button>
</div>
<div style={{ margin: '10px', width: '300px' }}>
<List
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
</div>
);
}
}
export default TodoList;
10.通过input体验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={{ width: '250px', marginRight: '10px' }}
onChange={this.changeInputValue}
value={this.state.inputValue}
></Input>
<Button type="primary" onClick={this.clickBtn}>增加</Button>
</div>
<div style={{ margin: '10px', width: '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内容 发生变化时,界面也会相应发生变化。如果将订阅的代码去掉,则界面不会发生变化。