1、拆分文件
redux整个流程(简版):
1、先在action-types.js里定功能
2、组件
3、reducer
4、合并reducer
5、产生store
6、渲染组件并运行文件
安装redux
yarn add redux
目录:

src/components/Counter.js
import React,{Component} from 'react';
import store from '../store';
export default class Counter extends React.Component {
render(){
return (
<div>{store.getState().counter.num}</div>
)
}
}
src/components/Todo.js
import React,{Component} from 'react';
import store from '../store';
export default class Todo extends React.Component {
render(){
return (
<div>TODO</div>
)
}
}
src/store/reducer/counter.js
import * as Types from '../action-types'
function counter(state={num:100},action) {
switch (action.type){
case Types.INCREMENT:
return {num:state.num+action.count}
case Types.DECREMENT:
return {num:state.num-action.count}
}
return state;
}
// 这里由于counter.js是专门用来处理counter的所以可以默认导出
export default counter
src/store/reducer/todo.js
import * as Types from '../action-types';
function todo(state=[],action) {
switch (action.type){
case Types.ADD_TODO:
return [...state,action.text];
}
return state;
}
export default todo;
src/store/reducer/index.js
// 合并reducer
import counter from './counter';
import todo from './todo';
import {combineReducers} from "redux";
// 会产生一个新的reducer
let reducers = combineReducers({
counter,todo
});// {counter:{num:0},todo:[]}
export default reducers;
src/store/action-types.js
// 通过export导出 import * as Types form './action-types'引入 通过Types.INCREMENT引用
export const INCREMENT = 'INCREMENT';//第一个要写的功能是增加
export const DECREMENT = 'DECREMENT';//第二个要写的功能是减少
//这里我们将todo的逻辑和计数器的逻辑写一起 因为就只有这三个功能
// 功能多,逻辑多的时候就拆分开写
export const ADD_TODO = 'ADD_TODO';
src/store/index.js
import reducer from './reducer';
import {createStore} from "redux";
//创建一个store
export default createStore(reducer);
src/index.js
import React,{Component} from 'react';
import ReactDOM,{render} from 'react-dom';
import Counter from './components/Counter';
ReactDOM.render(<div>
<Counter/>
</div>,window.root);
流程图:

计数器和todolist列子
目录:

src/components/Counter.js
import React,{Component} from 'react';
import store from '../store';
import * as actions from '../store/action/counter'
// import * as Types from '../store/action-types';
// // ActionCreator 就是一个普通函数 返回一个action对象 这段代码抽出到action counter.js中去
// function add(count) {
// return {type:Types.INCREMENT,count}
// }
// function minus(count) {
// return {type:Types.DECREMENT,count}
// }
export default class Counter extends React.Component {
constructor(){
super();
this.state = {n:store.getState().counter.num}
}
componentDidMount(){
store.subscribe(()=>{
this.setState({n:store.getState().counter.num});
})
}
render(){
return (
<div>
<button onClick={()=>{
store.dispatch(actions.add(10));
}}>+</button>
{/*{store.getState().counter.num}*/}
{this.state.n}
<button onClick={()=>{
store.dispatch(actions.minus(10));
}}>-</button>
</div>
)
}
}
src/components/Todo.js
import React,{Component} from 'react';
import store from '../store';
import actions from '../store/action/todo';
export default class Todo extends React.Component {
constructor(){
super();
this.state = {todos:store.getState().todo}
}
componentDidMount(){
store.subscribe(()=>{
this.setState({todos:store.getState().todo})
})
}
render(){
return (
<div>
<input type="text" onKeyUp={(e)=>{
if(e.keyCode == 13){
store.dispatch(actions.addTodo(e.target.value));
}
}}/>
{this.state.todos.map((item,index)=>(<li key={index}>{item}</li>))}
</div>
)
}
}
src/store/action-types.js
// 通过export导出 import * as Types form './action-types'引入
export const INCREMENT = 'INCREMENT';//第一个要写的功能是增加
export const DECREMENT = 'DECREMENT';//第二个要写的功能是减少
//这里我们将todo的逻辑和计数器的逻辑写一起 因为就只有这三个功能
// 功能多,逻辑多的时候就拆分开写
export const ADD_TODO = 'ADD_TODO';
src/store/index.js
import reducer from './reducer';
import {createStore} from "redux";
//创建一个store
export default createStore(reducer);
src/store/reducer/counter.js
import * as Types from '../action-types'
function counter(state={num:100},action) {
switch (action.type){
case Types.INCREMENT:
return {num:state.num+action.count}
case Types.DECREMENT:
return {num:state.num-action.count}
}
return state;
}
// 这里由于counter.js是专门用来处理counter的所以可以默认导出
export default counter
src/store/reducer/todo.js
import * as Types from '../action-types';
function todo(state=[],action) {
switch (action.type){
case Types.ADD_TODO:
return [...state,action.text];
}
return state;
}
export default todo;
src/store/reducer/index.js
// 合并reducer
import counter from './counter';
import todo from './todo';
import {combineReducers} from "redux";
// combineReducers的实现原理
// 会产生一个新的reducer
// let myCombine = (reducers)=>{
// //参数reducers就是combineReducers里的对象{counter,todo} counter和todo是函数
// return (state={},action)=>{
// let obj = {}
// for (let key in reducers){
// obj[key] = reducers[key](state[key],action)
// }
// return obj
// }
// };
let reducers = combineReducers({
counter,todo
});// {counter:{num:0},todo:[]}
export default reducers;
src/store/action/counter.js
import * as Types from '../action-types';
// ActionCreator 就是一个普通函数 返回一个action对象
function add(count) {
return {type:Types.INCREMENT,count}
}
function minus(count) {
return {type:Types.DECREMENT,count}
}
export {add,minus}
src/store/action/todo.js
import * as Types from '../action-types';
let obj = {
addTodo(content){
return {type:Types.ADD_TODO,text:content}
}
};
export default obj;
src/index.js
import React,{Component} from 'react';
import ReactDOM,{render} from 'react-dom';
import Counter from './components/Counter';
import Todo from "./components/Todo";
ReactDOM.render(<div>
<Counter/>
<Todo/>
</div>,window.root);
2、引入react-redux
在以上:计数器和todolist列子之上
由于每次需要把状态变成自己的,再去改这个状态,就很麻 => 如图

所以:安装 yarn add react-redux

react-redux提供了一个Provider组件,使用这个组件
src/index.js
import React,{Component} from 'react';
import ReactDOM,{render} from 'react-dom';
import Counter from './components/Counter';
import Todo from "./components/Todo";
// 1、react-redux提供了一个Provider组件 这个组件需要传入store 所以引入react-redux和store
import store from './store';
import {Provider} from 'react-redux';
// 2、使用Provider组件,这个组件只能有一个元素 传入store
ReactDOM.render(<Provider store={store}>
<div>
<Counter/>
<Todo/>
</div>
</Provider>,window.root);
进到:src/components/Counter.js
import React,{Component} from 'react';
// 1、组件外面(index.js的Provider)传入了stroe,所以不需要引入
// import store from '../store';
import * as actions from '../store/action/counter'
// 3、需要引入connect连接的方法取出redux中的数据
import {connect} from 'react-redux';
// 4、利用react-redux需要导出一个连接后的组件
// export default class Counter extends React.Component {
class Counter extends React.Component {
// 2、状态和componentDidMount都不需要 所以渲染render的state部分也都注释
// constructor(){
// super();
// this.state = {n:store.getState().counter.num}
// }
// componentDidMount(){
// store.subscribe(()=>{
// this.setState({n:store.getState().counter.num});
// })
// }
render(){
return (
<div>
<button onClick={()=>{
// store.dispatch(actions.add(10));
this.props.add(1);
}}>+</button>
{/* {this.state.n} */}
{/* <p>{this.props.n1}</p> */}
<p>{this.props.num}</p>
<button onClick={()=>{
// store.dispatch(actions.minus(10));
this.props.minus(2);
}}>-</button>
</div>
)
}
}
// // 4、导出一个连接后的组件 开始
// // connect执行时有两个“函数” mapStateToProps、mapDispatchToProps
// // 4-1、mapStateToProps 将redux中的状态映射成属性 传递给第二个参数(当前组件)
// // 4-2、mapDispatchToProps 将dispatch方法映射成属性 传递给第二个参数(当前组件)
// // 4-3、这两个组件的返回值会作为当前组件的属性
// let mapStateToProps = (state) =>{ // 这个函数的参数是state
// return {n1:state.counter.num}
// // return {...state.counter} // {...state.counter} = > {num:0}
// };
// let mapDispatchToProps = (dispatch) =>{ // 这个函数的参数是dispatch
// return {
// add:(count)=>{
// dispatch(actions.add(count));
// },
// minus:(count)=>{
// dispatch(actions.minus(count));
// }
// }
// };
// export default connect(mapStateToProps,mapDispatchToProps)(Counter);// 第二次执行的参数是当前组件
// // 4、导出一个连接后的组件 结束
// 5、简化4的写法
// connect中的mapDispatchToProps可以传入一个actionCreator对象
export default connect(state=>({...state.counter}),actions)(Counter);// 第二次执行的参数是当前组件
// // 5_1、中actiopns是如何实现,去匹配mapDispatchToProps中的函数(使用actions后,mapDispatchToProps中返回的函数名add和minus不能改名)
// let bindActionCreators = (actions) =>{//为什么可以直接传入一个actions,在内部会用这个函数进行包装
// return(dispatch)=>{
// let obj = {};
// for(let key in actions){
// obj[key] = (...args)=>{
// dispatch(actions[key](...args))
// }
// }
// return obj;
// }
// }
// export default connect(state=>({...state.counter}),bindActionCreators(actions))(Counter);
// // 5_1、以上这一段代码就是4、 mapDispatchToProps 中的实现也是5、中直接传入actions的实现 这个方法时redux中的
进到:src/components/Todo.js
import React,{Component} from 'react';
// 1、组件外面(index.js的Provider)传入了stroe,所以不需要引入
// import store from '../store';
import actions from '../store/action/todo';
// 3、需要引入connect连接的方法取出redux中的数据
import {connect} from 'react-redux';
// 4、利用react-redux需要导出一个连接后的组件
// export default class Todo extends React.Component {
class Todo extends React.Component {
// 2、状态和componentDidMount都不需要 所以渲染render的state部分也都注释
// constructor(){
// super();
// this.state = {todos:store.getState().todo}
// }
// componentDidMount(){
// store.subscribe(()=>{
// this.setState({todos:store.getState().todo})
// })
// }
render(){
return (
<div>
<input type="text" onKeyUp={(e)=>{
if(e.keyCode == 13){
// store.dispatch(actions.addTodo(e.target.value));
this.props.addTodo(e.target.value);
e.target.value = " "
}
}}/>
{/* {this.state.todos.map((item,index)=>(<li key={index}>{item}</li>))} */}
{this.props.todos.map((item,index)=>(<li key={index}>{item}</li>))}
</div>
)
}
}
// {counter:{num:0},todo:[]}
// 这里todo:[]是数组,不能解构,只有通过 => 把状态里的todo映射到Todo组件的todos属性里
export default connect(state=>({todos:state.todo}),actions)(Todo)
//4、导出一个连接后的组件 结束
// // 4_1、中actiopns是如何实现,去匹配mapDispatchToProps中的函数(使用actions后,mapDispatchToProps中返回的函数名add和minus不能改名)
// let bindActionCreators = (actions) =>{//为什么可以直接传入一个actions,在内部会用这个函数进行包装
// return(dispatch)=>{
// let obj = {};
// for(let key in actions){
// obj[key] = (...args)=>{
// dispatch(actions[key](...args))
// }
// }
// return obj;
// }
// }
// export default connect(state=>({...state.counter}),bindActionCreators(actions))(Counter);
// // 4_1、以上这一段代码就是4、 mapDispatchToProps 中的实现也是5、中直接传入actions的实现 这个方法时redux中的
除以上代码,其他代码逻辑不变。

3-6、react-redux之todo
目录

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import TodoHeader from './TodoHeader';
import TodoList from './TodoList';
import TodoFooter from './TodoFooter';
export default class App extends React.Component {
constructor() {
super();
}
render() {
return (
<div className='container'>
<div className='row'>
<div className='col-md-6 col-md-offset-3'>
<div className='panel panel-danger'>
<div className='panel-heading'><TodoHeader /></div>
<div className='panel-body'><TodoList /></div>
<div className='panel-footer'><TodoFooter /></div>
</div>
</div>
</div>
</div>
)
}
}
src/components/TodoFooter.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import actions from '../store/action/index';
class TodoFooter extends React.Component {
render() {
return (
<div>
{/* {this.props.type} // 测试 */}
<nav className='nav nav-pills' onClick={(e)=>{
let result = e.target.dataset.type;//点击的某一个
this.props.changeType(result);
}}>
<li className={this.props.type==='all'?'active':''}><a data-type='all'>全部</a></li>
<li className={this.props.type==='unfinish'?'active':''}><a data-type='unfinish'>未完成</a></li>
<li className={this.props.type==='finish'?'active':''}><a data-type='finish'>已完成</a></li>
</nav>
</div>
)
}
}
export default connect(state=>({...state}),actions)(TodoFooter);
src/components/TodoHeader.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
// actions是actionCreator组成的对象
import actions from '../store/action/index'
class TodoHeader extends React.Component {
getUnFinishCount = () =>{ // 过滤数组中没有选中的length
return this.props.todos.filter(item=>!item.isSelected).length
}
render() {
return <div >
<h3>亲 你有{this.getUnFinishCount()}件事没完成 </h3>
<input type='text' className='form-control' onKeyUp={(e)=>{
if(e.keyCode === 13){
this.props.addTodo({id:Math.random(),title:e.target.value,
isSelected:false})
}
}}/>
</div>
}
}
// store.getState();//{todos:[{title:'xxx'}]}
export default connect(state=>({...state}),actions)(TodoHeader)
src/components/TodoList.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import actions from '../store/action/index';
import TodoItem from './TodoItem';
class TodoList extends React.Component {
filterData() {
let todos = [];
if (this.props.type === 'all') {
todos = this.props.todos
} else if (this.props.type === 'finish') {
todos = this.props.todos.filter(item => item.isSelected)
} else {
todos = this.props.todos.filter(item => !item.isSelected)
}
return todos;
}
render() {
return <div>
<ul className='list-group'>
{/* <li className='list-group-item'>
<input type='checkbox'/>
今天吃药了吗?
<button className='btn btn-xs pull-right'>×</button>
</li> */}
{/* {this.props.todos.map((item,index)=>( */}
{this.filterData().map((item, index) => (
<TodoItem
key={index}
item={item}
changeSelected={(id)=>{
this.props.changeSelected(id);
}}
deleteTodo={(id)=>{
this.props.deleteTodo(id);
}}
/>
))}
</ul>
</div>
}
}
// 一般情况下 组件分为两类 智能组件(聪明组件),通过redux获取数据
// 木偶组件(傻的组件,ui组件),只负责展示
export default connect(state => ({ ...state }), actions)(TodoList);
src/components/TodoItem.js
import React, { Component } from 'react';
export default class App extends Component {
render() {
let {item} = this.props;
return <li className='list-group-item'>
<input type='checkbox' checked={item.isSelected} onChange={() => {
this.props.changeSelected(item.id);
}} />
{item.title}
<button className='btn btn-xs pull-right' onClick={() => {
this.props.deleteTodo(item.id);
}}>×</button>
</li>
}
}
src/store/action/index.js
import * as Types from '../action-types';
// actionCreator的对象
let actions = {
addTodo(todo){ // todo是要添加的内容{title,id,isSelected}
return {type:Types.ADD_TODO,todo}
},
changeSelected(id){ // 告诉我当前是哪个checkbox更改了
return {type:Types.CHANGE_SELECTED,id}
},
deleteTodo(id){
return {type:Types.DELETE_TODO,id}
},
changeType(val){
return {type:Types.CHANGE_CURRENT_TYPE,val}
}
};
export default actions;
src/store/reducer/index.js
import * as Types from '../action-types'
// 管理员
let initState = {
type:'all',//默认全部显示
todos:[
{isSelected:false,title:'今天吃药了吗?',id:1},
{isSelected:true,title:'今天吃药了吗?',id:2}
]
};
function reducer(state=initState,action){
switch (action.type){
case Types.ADD_TODO:
return {...state,todos:[...state.todos,action.todo]};
case Types.CHANGE_SELECTED:
let todos = state.todos.map(item=>{
if(item.id === action.id){// 找到id和当前选择id相等一项的
item.isSelected = !item.isSelected;// 将状态取反
}
return item;
})
return {...state,todos}
case Types.DELETE_TODO:{
let todos = state.todos.filter(item=>item.id!=action.id);
return {...state,todos}
}
case Types.CHANGE_CURRENT_TYPE:
return {...state,type:action.val}
}
return state;
}
export default reducer;
src/store/action-types.js
export const ADD_TODO = 'ADD_TODO';// 添加
export const CHANGE_SELECTED = 'CHANGE_SELECTED';// 改变选择状态
export const DELETE_TODO = 'DELETE_TODO';// 删除某个todo
export const CHANGE_CURRENT_TYPE = 'CHANGE_CURRENT_TYPE';
src/store/index.js
import {createStore} from 'redux';
import reducer from './reducer';
let store = createStore(reducer);
window._store = store;
// 为了在控制台里可以打印store中的内容
export default store;
src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import App from "./components/App";
import 'bootstrap/dist/css/bootstrap.css';
import {Provider} from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>
,window.root);

最后
