React 求和案例--react-redux

759 阅读16分钟

尚硅谷

1. react-redux的基本使用

{7fabb95c-8472-4464-8b34-7dea96fd0721}.png

UI组件--components-Count

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 通过props接收数据(一般数据和函数)
  • 不使用任何 Redux 的 API
import React, { Component } from 'react'

export default class Count extends Component {

    state = {carName:'奔驰c63'}

    //加法
    increment = ()=>{
            const {value} = this.selectNumber
            this.props.jia(value*1)
    }
    //减法
    decrement = ()=>{
            const {value} = this.selectNumber
            this.props.jian(value*1)
    }
    //奇数再加
    incrementIfOdd = ()=>{
            const {value} = this.selectNumber
            if(this.props.count % 2 !== 0){
                    this.props.jia(value*1)
            }
    }
    //异步加
    incrementAsync = ()=>{
            const {value} = this.selectNumber
            this.props.jiaAsync(value*1,500)
    }

    render() {
            //console.log('UI组件接收到的props是',this.props);
            return (
                    <div>
                            <h1>当前求和为:{this.props.count}</h1>
                            <select ref={c => this.selectNumber = c}>
                                    <option value="1">1</option>
                                    <option value="2">2</option>
                                    <option value="3">3</option>
                            </select>&nbsp;
    <button onClick={this.increment}>+</button>&nbsp;
    <button onClick={this.decrement}>-</button>&nbsp;
    <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
    <button onClick={this.incrementAsync}>异步加</button>&nbsp;
                    </div>
            )
    }
}

容器组件-containers-Count

  • 负责管理数据和业务逻辑,不负责UI的呈现
  • 使用 Redux 的 API
  • 一般保存在containers文件夹下
//引入Count的UI组件
import CountUI from '../../components/Count'
//引入action
import {
	createIncrementAction,
	createDecrementAction,
	createIncrementAsyncAction
} from '../../redux/count_action'

//引入connect用于连接UI组件与redux
import { connect } from 'react-redux'

/* 
1.mapStateToProps函数返回的是一个对象;
2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
3.mapStateToProps用于传递状态
*/
function mapStateToProps(state) {
	return { count: state }
}

/* 
1.mapDispatchToProps函数返回的是一个对象;
2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
3.mapDispatchToProps用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch) {
	return {
		jia: number => dispatch(createIncrementAction(number)),
		jian: number => dispatch(createDecrementAction(number)),
		jiaAsync: (number, time) => dispatch(createIncrementAsyncAction(number, time)),
	}
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)

App.jsx

import React, { Component } from 'react'
// 注意,这里引入containers组件下的Count,即Count的容器组件
import Count from './containers/Count'
import store from './redux/store'

export default class App extends Component {
	render() {
		return (
			<div>
				{/* 给容器组件传递store */}
				<Count store={store} />
			</div>
		)
	}
}

redux

constant.js

/* 
该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

count_action.js

/* 
	该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from './constant'

//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const createIncrementAsyncAction = (data,time) => {
	return (dispatch)=>{
		setTimeout(()=>{
			dispatch(createIncrementAction(data))
		},time)
	}
}

count_reducer.js

/* 
    1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
    2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT,DECREMENT} from './constant'

const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
	// console.log(preState);
	//从action对象中获取:type、data
	const {type,data} = action
	//根据type决定如何加工数据
	switch (type) {
		case INCREMENT: //如果是加
			return 
                        preState +  
		case DECREMENT: //若果是减
			return preState - data
		default:
			return preState
	}
}

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))

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'

ReactDOM.render(<App/>,document.getElementById('root'))

//监测redux中状态的改变,如redux的状态发生了改变,那么重新渲染App组件
store.subscribe(()=>{
    ReactDOM.render(<App/>,document.getElementById('root'))
})

2. react-redux优化

{c946dba3-bf23-4ba7-87ef-9176e273da6f}.png

容器组件和UI组件整合成一个组件-containers-Count

import React, { Component } from 'react'
//引入action
import {
    createIncrementAction,
    createDecrementAction,
    createIncrementAsyncAction
} from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import { connect } from 'react-redux'

//定义UI组件
class Count extends Component {

    state = { carName: '奔驰c63' }

    //加法
    increment = () => {
            const { value } = this.selectNumber
            this.props.jia(value * 1)
    }
    //减法
    decrement = () => {
            const { value } = this.selectNumber
            this.props.jian(value * 1)
    }
    //奇数再加
    incrementIfOdd = () => {
            const { value } = this.selectNumber
            if (this.props.count % 2 !== 0) {
                    this.props.jia(value * 1)
            }
    }
    //异步加
    incrementAsync = () => {
            const { value } = this.selectNumber
            this.props.jiaAsync(value * 1, 500)
    }

    render() {
            //console.log('UI组件接收到的props是',this.props);
            return (
                    <div>
                            <h1>当前求和为:{this.props.count}</h1>
                            <select ref={c => this.selectNumber = c}>
                                    <option value="1">1</option>
                                    <option value="2">2</option>
                                    <option value="3">3</option>
                            </select>&nbsp;
    <button onClick={this.increment}>+</button>&nbsp;
    <button onClick={this.decrement}>-</button>&nbsp;
    <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
    <button onClick={this.incrementAsync}>异步加</button>&nbsp;
                    </div>
            )
    }
}

//使用connect()()创建并暴露一个Count的容器组件
//简写写法:直接将函数体写在connect中,同时将mapDispatchToProps简写,可以省略dispatch的调用
export default connect(
    state => ({ count: state }),

    //mapDispatchToProps的一般写法
    /* dispatch => ({
            jia:number => dispatch(createIncrementAction(number)),
            jian:number => dispatch(createDecrementAction(number)),
            jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
    }) */

    //mapDispatchToProps的简写,这样写得到一个action对象,react-redux自带帮你dispatch
    {
            jia: createIncrementAction,
            jian: createDecrementAction,
            jiaAsync: createIncrementAsyncAction,
    }
)(Count)

App.jsx

无需自己给容器组件传递store,把所有容器都需要的store交给Provider,给包裹一个<Provider store={store}>即可。

import React, { Component } from 'react'
import Count from './containers/Count'

export default class App extends Component {
	render() {
		return (
			<div>
				<Count/>
			</div>
		)
	}
}

index.js

使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import { Provider } from 'react-redux'

ReactDOM.render(
	<Provider store={store}>
		<App />
	</Provider>,
	document.getElementById('root')
)

3. react-redux数据共享版

{2f9ca355-d7cc-4387-97b7-b12aea74a67a}.png

容器组件和UI组件整合成一个组件-containers-Count

import React, { Component } from 'react'
//引入action
import {
	createIncrementAction,
	createDecrementAction,
	createIncrementAsyncAction
} from '../../redux/actions/count'
//引入connect用于连接UI组件与redux
import { connect } from 'react-redux'

//定义UI组件
class Count extends Component {

	//加法
	increment = () => {
		const { value } = this.selectNumber
		this.props.jia(value * 1)
	}
	//减法
	decrement = () => {
		const { value } = this.selectNumber
		this.props.jian(value * 1)
	}
	//奇数再加
	incrementIfOdd = () => {
		const { value } = this.selectNumber
		if (this.props.count % 2 !== 0) {
			this.props.jia(value * 1)
		}
	}
	//异步加
	incrementAsync = () => {
		const { value } = this.selectNumber
		this.props.jiaAsync(value * 1, 500)
	}

	render() {
		//console.log('UI组件接收到的props是',this.props);
		return (
			<div>
				<h2>我是Count组件,下方组件总人数为:{this.props.renshu}</h2>
				<h4>当前求和为:{this.props.count}</h4>
				<select ref={c => this.selectNumber = c}>
					<option value="1">1</option>
					<option value="2">2</option>
					<option value="3">3</option>
				</select>&nbsp;
    <button onClick={this.increment}>+</button>&nbsp;
    <button onClick={this.decrement}>-</button>&nbsp;
    <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
    <button onClick={this.incrementAsync}>异步加</button>&nbsp;
			</div>
		)
	}
}

//使用connect()()创建并暴露一个Count的容器组件,和上面相比这里发生了变化
export default connect(
	state => ({
		count: state.he,
		renshu: state.rens.length
	}),
	{
		jia: createIncrementAction,
		jian: createDecrementAction,
		jiaAsync: createIncrementAsyncAction,
	}
)(Count)

容器组件和UI组件整合成一个组件-containers-Person

import React, { Component } from 'react'
import { nanoid } from 'nanoid'
import { connect } from 'react-redux'
import { createAddPersonAction } from '../../redux/actions/person'

class Person extends Component {

	addPerson = () => {
		const name = this.nameNode.value
		const age = this.ageNode.value
		const personObj = { id: nanoid(), name: name, age: age }
		this.props.jiaYiRen(personObj)
		this.nameNode.value = ''
		this.ageNode.value = ''
	}

    render() {
        return (
            <div>
                <h2>我是Person组件,上方组件求和为{this.props.he}</h2>
                <input ref={c => this.nameNode = c} type="text" placeholder="输入名字" />
                <input ref={c => this.ageNode = c} type="text" placeholder="输入年龄" />
                <button onClick={this.addPerson}>添加</button>
                <ul>
                    {
                        this.props.yiduiren.map((p) => {
                                return <li key={p.id}>{p.name}--{p.age}</li>
                        })
                    }
                </ul>
            </div>
        )
    }
}

export default connect(
	state => ({ yiduiren: state.rens, he: state.he }),//映射状态
	{ jiaYiRen: createAddPersonAction }//映射操作状态的方法
)(Person)

redux-action-count.js

/* 
	该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from '../constant'

//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const createIncrementAsyncAction = (data,time) => {
	return (dispatch)=>{
		setTimeout(()=>{
			dispatch(createIncrementAction(data))
		},time)
	}
}

redux-action-person.js

import {ADD_PERSON} from '../constant'

//创建增加一个人的action动作对象
export const createAddPersonAction = personObj => ({type:ADD_PERSON,data:personObj})

redux-reducers-count.js

/* 
    1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
    2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT,DECREMENT} from '../constant'

const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
	// console.log('countReducer@#@#@#');
	//从action对象中获取:type、data
	const {type,data} = action
	//根据type决定如何加工数据
	switch (type) {
		case INCREMENT: //如果是加
			return preState + data
		case DECREMENT: //若果是减
			return preState - data
		default:
			return preState
	}
}

redux-reducers-person.js

import {ADD_PERSON} from '../constant'

//初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]

export default function personReducer(preState=initState,action){
	// console.log('personReducer@#@#@#');
	const {type,data} = action
	switch (type) {
		case ADD_PERSON: //若是添加一个人
			return [data,...preState]
		default:
			return preState
	}
}

constant.js

/* 
该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON = 'add_person'

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,对象内部key:value值为state中的状态
const allReducer = combineReducers({
	he: countReducer,
	rens: personReducer
})

//暴露store
export default createStore(allReducer, applyMiddleware(thunk))

App.jsx

import React, { Component } from 'react'
import Count from './containers/Count' //引入的是Count的容器组件,不是UI组件
import Person from './containers/Person'//引入的是Person的容器组件,不是UI组件

export default class App extends Component {
	render() {
		return (
			<div>
				<Count/>
				<hr/>
				<Person/>
			</div>
		)
	}
}

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import {Provider} from 'react-redux'

ReactDOM.render(
	<Provider store={store}>
		<App/>
	</Provider>,
	document.getElementById('root')
)

4. react-redux最终版-完善了代码规范

{fc952375-e953-4255-b1b2-1d1cfcec659e}.png

容器组件和UI组件整合成一个组件-containers-Count

import React, { Component } from 'react'
//引入action
import {
	increment,
	decrement,
	incrementAsync
} from '../../redux/actions/count'
//引入connect用于连接UI组件与redux
import { connect } from 'react-redux'

//定义UI组件
class Count extends Component {

	state = { carName: '奔驰c63' }

	//加法
	increment = () => {
		const { value } = this.selectNumber
		this.props.increment(value * 1)
	}
	//减法
	decrement = () => {
		const { value } = this.selectNumber
		this.props.decrement(value * 1)
	}
	//奇数再加
	incrementIfOdd = () => {
		const { value } = this.selectNumber
		if (this.props.count % 2 !== 0) {
			this.props.increment(value * 1)
		}
	}
	//异步加
	incrementAsync = () => {
		const { value } = this.selectNumber
		this.props.incrementAsync(value * 1, 500)
	}

	render() {
		//console.log('UI组件接收到的props是',this.props);
		return (
			<div>
				<h2>我是Count组件,下方组件总人数为:{this.props.personCount}</h2>
				<h4>当前求和为:{this.props.count}</h4>
				<select ref={c => this.selectNumber = c}>
					<option value="1">1</option>
					<option value="2">2</option>
					<option value="3">3</option>
				</select>&nbsp;
				<button onClick={this.increment}>+</button>&nbsp;
				<button onClick={this.decrement}>-</button>&nbsp;
				<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
				<button onClick={this.incrementAsync}>异步加</button>&nbsp;
			</div>
		)
	}
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(
	state => ({
		count: state.count,
		personCount: state.persons.length
	}),
	{
		increment: increment,
		decrement: decrement,
		incrementAsync: incrementAsync
	}
)(Count)

容器组件和UI组件整合成一个组件-containers-Person

import React, { Component } from 'react'
import { nanoid } from 'nanoid'
import { connect } from 'react-redux'
import { addPerson } from '../../redux/actions/person'

class Person extends Component {

	addPerson = () => {
		const name = this.nameNode.value
		const age = this.ageNode.value * 1
		const personObj = { id: nanoid(), name, age }
		this.props.addPerson(personObj)
		this.nameNode.value = ''
		this.ageNode.value = ''
	}

	render() {
		return (
			<div>
				<h2>我是Person组件,上方组件求和为{this.props.count}</h2>
				<input ref={c => this.nameNode = c} type="text" placeholder="输入名字" />
				<input ref={c => this.ageNode = c} type="text" placeholder="输入年龄" />
				<button onClick={this.addPerson}>添加</button>
				<ul>
					{
						this.props.persons.map((p) => {
							return <li key={p.id}>{p.name}--{p.age}</li>
						})
					}
				</ul>
			</div>
		)
	}
}

export default connect(
	state => ({
		persons: state.persons,
		count: state.count
	}),//映射状态
	{ addPerson: addPerson }//映射操作状态的方法
)(Person)

redux-action-count.js

/* 
	该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from '../constant'

//同步action,就是指action的值为Object类型的一般对象
export const increment = data => ({type:INCREMENT,data})
export const decrement = data => ({type:DECREMENT,data})

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const incrementAsync = (data,time) => {
	return (dispatch)=>{
		setTimeout(()=>{
			dispatch(increment(data))
		},time)
	}
}

redux-action-person.js

import {ADD_PERSON} from '../constant'

//创建增加一个人的action动作对象
export const addPerson = personObj => ({type:ADD_PERSON,data:personObj})

redux-reducers-count.js

/* 
    1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
    2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT,DECREMENT} from '../constant'

const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
	// console.log('countReducer@#@#@#');
	//从action对象中获取:type、data
	const {type,data} = action
	//根据type决定如何加工数据
	switch (type) {
		case INCREMENT: //如果是加
			return preState + data
		case DECREMENT: //若果是减
			return preState - data
		default:
			return preState
	}
}

redux-reducers-person.js

import {ADD_PERSON} from '../constant'

//初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]

export default function personReducer(preState=initState,action){
	// console.log('personReducer@#@#@#');
	const {type,data} = action
	switch (type) {
		case ADD_PERSON: //若是添加一个人
			return [data,...preState]
		default:
			return preState
	}
}

redux-reducers-index.js

/* 
	该文件用于汇总所有的reducer为一个总的reducer
*/
//引入combineReducers,用于汇总多个reducer
import { combineReducers } from 'redux'
//引入为Count组件服务的reducer
import count from './count'
//引入为Person组件服务的reducer
import persons from './person'

//汇总所有的reducer变为一个总的reducer
export default combineReducers({
	count: count,
	persons: persons
})

store.js

/* 
	该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入汇总之后的reducer
import reducer from './reducers'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//引入redux-devtools-extension,redux开发者工具
import {composeWithDevTools} from 'redux-devtools-extension'

//暴露store 
export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))

codewhy

1. 基础版

1640855987(1).png 1640857965(1).png

page/about:

import React from "react";

import { connect } from "react-redux";

import { decAction, subAction } from "../store/actionCreators";

function About(props) {
  // console.log("About页面重新渲染了");
  return (
    <div>
      <hr />
      <h1>About</h1>
      <h2>当前计数: {props.counter}</h2>
      <button onClick={(e) => props.decrement()}>-1</button>
      <button onClick={(e) => props.subNumber(5)}>-5</button>
      <h1>Banner</h1>
      <ul>
        {props.banners.map((item, index) => {
          return <li key={item.acm}>{item.title}</li>;
        })}
      </ul>
      <h1>Recommend</h1>
      <ul>
        {props.recommends.map((item, index) => {
          return <li key={item.acm}>{item.title}</li>;
        })}
      </ul>
    </div>
  );
}

const mapStateToProps = (state) => {
  return {
    banners: state.banners,
    recommends: state.recommends,
    counter: state.counter,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    decrement: function () {
      dispatch(decAction());
    },
    subNumber: function (num) {
      dispatch(subAction(num));
    },
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(About);

page/home:

import React, { PureComponent } from "react";

import { connect } from "react-redux";

import axios from "axios";

import {
  incAction,
  addAction,
  changeBannersAction,
  changeRecommendAction,
} from "../store/actionCreators";

class Home extends PureComponent {
  componentDidMount() {
    axios({
      url: "http://123.207.32.32:8000/home/multidata",
    }).then((res) => {
      const data = res.data.data;
      this.props.changeBanners(data.banner.list);
      this.props.changeRecommends(data.recommend.list);
    });
  }

  render() {
    return (
      <div>
        <h1>Home</h1>
        <h2>当前计数: {this.props.counter}</h2>
        <button onClick={(e) => this.props.increment()}>+1</button>
        <button onClick={(e) => this.props.addNumber(5)}>+5</button>
      </div>
    );
  }
}
// 相比about组件,简写形式
const mapStateToProps = (state) => ({
  counter: state.counter,
});

const mapDispatchToProps = (dispatch) => ({
  increment() {
    dispatch(incAction());
  },
  addNumber(num) {
    dispatch(addAction(num));
  },
  changeBanners(banners) {
    dispatch(changeBannersAction(banners));
  },
  changeRecommends(recommends) {
    dispatch(changeRecommendAction(recommends));
  },
});

export default connect(mapStateToProps, mapDispatchToProps)(Home);

actionCreators.js

import axios from "axios";

import {
  ADD_NUMBER,
  SUB_NUMBER,
  INCREMENT,
  DECREMENT,
  CHANGE_BANNERS,
  CHANGE_RECOMMEND,
} from "./constants.js";


// 简写
export const addAction = (num) => ({
  type: ADD_NUMBER,
  num,
});

export const subAction = (num) => ({
  type: SUB_NUMBER,
  num,
});

export const incAction = () => ({
  type: INCREMENT,
});

export const decAction = () => ({
  type: DECREMENT,
});

// 轮播图和推荐的action
export const changeBannersAction = (banners) => ({
  type: CHANGE_BANNERS,
  banners,
});

export const changeRecommendAction = (recommends) => ({
  type: CHANGE_RECOMMEND,
  recommends,
});

constants.js

export const ADD_NUMBER = "ADD_NUMBER";
export const SUB_NUMBER = "SUB_NUMBER";
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";

export const CHANGE_BANNERS = "CHANGE_BANNERS";
export const CHANGE_RECOMMEND = "CHANGE_RECOMMEND";

index.js/store

import { createStore } from "redux";

import reducer from "./reducer.js";

const store = createStore(reducer);
export default store;

reducer.js

import {
  ADD_NUMBER,
  SUB_NUMBER,
  INCREMENT,
  DECREMENT,
  CHANGE_BANNERS,
  CHANGE_RECOMMEND
} from './constants.js';

const defaultState = {
  counter: 0,
  banners: [],
  recommends: []
}

function reducer(state = defaultState, action) {
  switch (action.type) {
    case ADD_NUMBER:
      return { ...state, counter: state.counter + action.num };
    case SUB_NUMBER:
      return { ...state, counter: state.counter - action.num };
    case INCREMENT:
      return { ...state, counter: state.counter + 1 };
    case DECREMENT:
      return { ...state, counter: state.counter - 1 };
    case CHANGE_BANNERS:
      return { ...state, banners: action.banners };
    case CHANGE_RECOMMEND:
      return { ...state, recommends: action.recommends };
    default:
      return state;
  }
}

export default reducer;

App.js

import React, { PureComponent } from "react";

import Home from "./pages/home3";
import About from "./pages/about3";
export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Home />
        <About />
      </div>
    );
  }
}

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import store from './store';
import { Provider } from 'react-redux';
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

2. 中间件-thunk

page/about:

import React from "react";

import { connect } from "react-redux";

import { decAction, subAction } from "../store/actionCreators";

function About(props) {
  // console.log("About页面重新渲染了");
  return (
    <div>
      <hr />
      <h1>About</h1>
      <h2>当前计数: {props.counter}</h2>
      <button onClick={(e) => props.decrement()}>-1</button>
      <button onClick={(e) => props.subNumber(5)}>-5</button>
      <h1>Banner</h1>
      <ul>
        {props.banners.map((item, index) => {
          return <li key={item.acm}>{item.title}</li>;
        })}
      </ul>
      <h1>Recommend</h1>
      <ul>
        {props.recommends.map((item, index) => {
          return <li key={item.acm}>{item.title}</li>;
        })}
      </ul>
    </div>
  );
}

const mapStateToProps = (state) => {
  return {
    banners: state.banners,
    recommends: state.recommends,
    counter: state.counter,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    decrement: function () {
      dispatch(decAction());
    },
    subNumber: function (num) {
      dispatch(subAction(num));
    },
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(About);

page/home:

import React, { PureComponent } from "react";

import { connect } from "react-redux";

import {
  incAction,
  addAction,
  getHomeMultidataAction,
} from "../store/actionCreators";

class Home extends PureComponent {
  componentDidMount() {
    this.props.getHomeMultidata();
  }

  render() {
    return (
      <div>
        <h1>Home</h1>
        <h2>当前计数: {this.props.counter}</h2>
        <button onClick={(e) => this.props.increment()}>+1</button>
        <button onClick={(e) => this.props.addNumber(5)}>+5</button>
      </div>
    );
  }
}

const mapStateToProps = (state) => ({
  counter: state.counter,
});

const mapDispatchToProps = (dispatch) => ({
  increment() {
    dispatch(incAction());
  },
  addNumber(num) {
    dispatch(addAction(num));
  },
  getHomeMultidata() {
    dispatch(getHomeMultidataAction());
  },
});

export default connect(mapStateToProps, mapDispatchToProps)(Home);

actionCreators.js

import axios from "axios";

import {
  ADD_NUMBER,
  SUB_NUMBER,
  INCREMENT,
  DECREMENT,
  CHANGE_BANNERS,
  CHANGE_RECOMMEND,
} from "./constants.js";

// 简写
export const addAction = (num) => ({
  type: ADD_NUMBER,
  num,
});

export const subAction = (num) => ({
  type: SUB_NUMBER,
  num,
});

export const incAction = () => ({
  type: INCREMENT,
});

export const decAction = () => ({
  type: DECREMENT,
});

// 轮播图和推荐的action
export const changeBannersAction = (banners) => ({
  type: CHANGE_BANNERS,
  banners,
});

export const changeRecommendAction = (recommends) => ({
  type: CHANGE_RECOMMEND,
  recommends,
});

// redux-thunk中定义的action函数
export const getHomeMultidataAction = (/*接收传入的参数*/) => {
  return (dispatch, getState) => {
    // getState--用于获取redux中的数据
    axios({
      url: "http://123.207.32.32:8000/home/multidata",
    }).then((res) => {
      const data = res.data.data;
      dispatch(changeBannersAction(data.banner.list));
      dispatch(changeRecommendAction(data.recommend.list));
    });
  };
};

constants.js

export const ADD_NUMBER = "ADD_NUMBER";
export const SUB_NUMBER = "SUB_NUMBER";
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";

export const CHANGE_BANNERS = "CHANGE_BANNERS";
export const CHANGE_RECOMMEND = "CHANGE_RECOMMEND";

index.js/store

import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";

import reducer from "./reducer.js";


const storeEnhancer = applyMiddleware(thunk);
const store = createStore(reducer, storeEnhancer);

export default store;

reducer.js

import {
  ADD_NUMBER,
  SUB_NUMBER,
  INCREMENT,
  DECREMENT,
  CHANGE_BANNERS,
  CHANGE_RECOMMEND
} from './constants.js';

const defaultState = {
  counter: 0,
  banners: [],
  recommends: []
}

function reducer(state = defaultState, action) {
  switch (action.type) {
    case ADD_NUMBER:
      return { ...state, counter: state.counter + action.num };
    case SUB_NUMBER:
      return { ...state, counter: state.counter - action.num };
    case INCREMENT:
      return { ...state, counter: state.counter + 1 };
    case DECREMENT:
      return { ...state, counter: state.counter - 1 };
    case CHANGE_BANNERS:
      return { ...state, banners: action.banners };
    case CHANGE_RECOMMEND:
      return { ...state, recommends: action.recommends };
    default:
      return state;
  }
}

export default reducer;

App.js

import React, { PureComponent } from "react";

import Home from "./pages/home4";
import About from "./pages/about3";
export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Home />
        <About />
      </div>
    );
  }
}

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import store from './store';
import { Provider } from 'react-redux';
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

3. 中间件-saga

page/about:

import React from "react";

import { connect } from "react-redux";

import { decAction, subAction } from "../store/actionCreators";

function About(props) {
  // console.log("About页面重新渲染了");
  return (
    <div>
      <hr />
      <h1>About</h1>
      <h2>当前计数: {props.counter}</h2>
      <button onClick={(e) => props.decrement()}>-1</button>
      <button onClick={(e) => props.subNumber(5)}>-5</button>
      <h1>Banner</h1>
      <ul>
        {props.banners.map((item, index) => {
          return <li key={item.acm}>{item.title}</li>;
        })}
      </ul>
      <h1>Recommend</h1>
      <ul>
        {props.recommends.map((item, index) => {
          return <li key={item.acm}>{item.title}</li>;
        })}
      </ul>
    </div>
  );
}

const mapStateToProps = (state) => {
  return {
    banners: state.banners,
    recommends: state.recommends,
    counter: state.counter,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    decrement: function () {
      dispatch(decAction());
    },
    subNumber: function (num) {
      dispatch(subAction(num));
    },
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(About);

page/home:

import React, { PureComponent } from 'react';

// import {connect} from '../utils/connect';
import { connect } from 'react-redux';


import {
  incAction,
  addAction,
  fetchHomeMultidataAction
} from '../store/actionCreators'

class Home extends PureComponent {
  componentDidMount() {
    this.props.getHomeMultidata();
  }

  render() {
    return (
      <div>
        <h1>Home</h1>
        <h2>当前计数: {this.props.counter}</h2>
        <button onClick={e => this.props.increment()}>+1</button>
        <button onClick={e => this.props.addNumber(5)}>+5</button>
      </div>
    )
  }
}

const mapStateToProps = state => ({
  counter: state.counter
})

const mapDispatchToProps = dispatch => ({
  increment() {
    dispatch(incAction());
  },
  addNumber(num) {
    dispatch(addAction(num));
  },
  getHomeMultidata() {
    dispatch(fetchHomeMultidataAction);
  }
})

export default connect(mapStateToProps, mapDispatchToProps)(Home);

actionCreators.js

import {
  ADD_NUMBER,
  SUB_NUMBER,
  INCREMENT,
  DECREMENT,
  CHANGE_BANNERS,
  CHANGE_RECOMMEND,
  FETCH_HOME_MULTIDATA,
} from "./constants.js";

// 简写
export const addAction = (num) => ({
  type: ADD_NUMBER,
  num,
});

export const subAction = (num) => ({
  type: SUB_NUMBER,
  num,
});

export const incAction = () => ({
  type: INCREMENT,
});

export const decAction = () => ({
  type: DECREMENT,
});

// 轮播图和推荐的action
export const changeBannersAction = (banners) => ({
  type: CHANGE_BANNERS,
  banners,
});

export const changeRecommendAction = (recommends) => ({
  type: CHANGE_RECOMMEND,
  recommends,
});

//  redux-saga拦截的action
export const fetchHomeMultidataAction = {
  type: FETCH_HOME_MULTIDATA,
};

constants.js

export const ADD_NUMBER = "ADD_NUMBER";
export const SUB_NUMBER = "SUB_NUMBER";
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";

export const FETCH_HOME_MULTIDATA = "FETCH_HOME_MULTIDATA";
export const CHANGE_BANNERS = "CHANGE_BANNERS";
export const CHANGE_RECOMMEND = "CHANGE_RECOMMEND";

index.js/store

import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
//1. 导入创建中间件的函数;
import createSagaMiddleware from "redux-saga";
import saga from "./saga";
import reducer from "./reducer.js";

//  composeEnhancers函数
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true }) || compose;

// 引入thunk中间件
// const storeEnhancer = applyMiddleware(thunk);

// 2.通过创建中间件的函数,创建中间件,并且放到applyMiddleware函数中;
const sagaMiddleware = createSagaMiddleware();

const storeEnhancer = applyMiddleware(thunk, sagaMiddleware);

const store = createStore(reducer, composeEnhancers(storeEnhancer));

//3.启动中间件的监听过程,并且传入要监听的saga;
sagaMiddleware.run(saga);

export default store;

reducer.js

import {
  ADD_NUMBER,
  SUB_NUMBER,
  INCREMENT,
  DECREMENT,
  CHANGE_BANNERS,
  CHANGE_RECOMMEND
} from './constants.js';

const defaultState = {
  counter: 0,
  banners: [],
  recommends: []
}

function reducer(state = defaultState, action) {
  switch (action.type) {
    case ADD_NUMBER:
      return { ...state, counter: state.counter + action.num };
    case SUB_NUMBER:
      return { ...state, counter: state.counter - action.num };
    case INCREMENT:
      return { ...state, counter: state.counter + 1 };
    case DECREMENT:
      return { ...state, counter: state.counter - 1 };
    case CHANGE_BANNERS:
      return { ...state, banners: action.banners };
    case CHANGE_RECOMMEND:
      return { ...state, recommends: action.recommends };
    default:
      return state;
  }
}

export default reducer;

saga

import { takeEvery, put, all, takeLatest } from "redux-saga/effects";
import axios from "axios";
import { FETCH_HOME_MULTIDATA, ADD_NUMBER } from "./constants";
import { changeBannersAction, changeRecommendAction } from "./actionCreators";

//3. 函数执行,进行异步操作,并调用对应的action
function* fetchHomeMultidata(action) {
  const res = yield axios.get("http://123.207.32.32:8000/home/multidata");
  const banners = res.data.data.banner.list;
  const recommends = res.data.data.recommend.list;
  // yield put(changeBannersAction(banners));
  // yield put(changeRecommendAction(recommends));
  yield all([
    yield put(changeBannersAction(banners)),
    yield put(changeRecommendAction(recommends)),
  ]);
}
//1. 封装一个生成器函数并暴露,内部拦截对应type的action
function* mySaga() {
  // takeLatest takeEvery区别:
  // takeLatest: 依次只能监听一个对应的action,也就是多个相同请求只执行最后一次
  // takeEvery: 每一次都会被执行
  yield all([
    // 2. takeLatest(要监听拦截的type, 拦截后调用的生成器函数)
    takeLatest(FETCH_HOME_MULTIDATA, fetchHomeMultidata),
    takeLatest(ADD_NUMBER, fetchHomeMultidata),
  ]);
}

export default mySaga;

App.js

import React, { PureComponent } from "react";

import Home from "./pages/home";
import About from "./pages/about";
export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Home />
        <About />
      </div>
    );
  }
}

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import store from './store';
import { Provider } from 'react-redux';
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

4.最终版--拆分目录结构

1640933522(1).png 1641262373(1).png

page/about:

import React from 'react';
// import { connect } from '../utils/connect';
import { connect } from 'react-redux';

import {
  decAction,
  subAction
} from '../store/counter/actionCreators';

function About(props) {
  return (
    <div>
      <hr />
      <h1>About</h1>
      <h2>当前计数: {props.counter}</h2>
      <button onClick={e => props.decrement()}>-1</button>
      <button onClick={e => props.subNumber(5)}>-5</button>
      <h1>Banner</h1>
      <ul>
        {
          props.banners.map((item, index) => {
            return <li key={item.acm}>{item.title}</li>
          })
        }
      </ul>
      <h1>Recommend</h1>
      <ul>
        {
          props.recommends.map((item, index) => {
            return <li key={item.acm}>{item.title}</li>
          })
        }
      </ul>
    </div>
  )
}

const mapStateToProps = state => {
  return {
    counter: state.counterInfo.counter,
    banners: state.homeInfo.banners,
    recommends: state.homeInfo.recommends
  }
};

const mapDispatchToProps = dispatch => {
  return {
    decrement: function () {
      dispatch(decAction());
    },
    subNumber: function (num) {
      dispatch(subAction(num))
    }
  }
};

export default connect(mapStateToProps, mapDispatchToProps)(About);

page/home:

import React, { PureComponent } from 'react';

import { connect } from 'react-redux';


import {
  incAction,
  addAction
} from '../store/counter/actionCreators';

import {
  fetchHomeMultidataAction
} from '../store/home/actionCreators';

class Home extends PureComponent {
  componentDidMount() {
    this.props.getHomeMultidata();
  }

  render() {
    return (
      <div>
        <h1>Home</h1>
        <h2>当前计数: {this.props.counter}</h2>
        <button onClick={e => this.props.increment()}>+1</button>
        <button onClick={e => this.props.addNumber(5)}>+5</button>
      </div>
    )
  }
}

const mapStateToProps = state => ({
  counter: state.counterInfo.counter
})

const mapDispatchToProps = dispatch => ({
  increment() {
    dispatch(incAction());
  },
  addNumber(num) {
    dispatch(addAction(num));
  },
  getHomeMultidata() {
    dispatch(fetchHomeMultidataAction);
  }
})

export default connect(mapStateToProps, mapDispatchToProps)(Home);

counter/actionCreators.js

import {
  ADD_NUMBER,
  SUB_NUMBER,
  INCREMENT,
  DECREMENT,
} from './constants.js';

export const addAction = num => ({
  type: ADD_NUMBER,
  num
});

export const subAction = num => ({
  type: SUB_NUMBER,
  num
});

export const incAction = () => ({
  type: INCREMENT
});

export const decAction = () => ({
  type: DECREMENT
});

counter/constants.js

export const ADD_NUMBER = "ADD_NUMBER";
export const SUB_NUMBER = "SUB_NUMBER";
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";

counter/index.js

import reducer from './reducer';

export {
  reducer
}
//可以将counter文件夹下所有的导出都集中到index.js中统一导出,更规范

counter/reducer.js

import {
  ADD_NUMBER,
  SUB_NUMBER,
  INCREMENT,
  DECREMENT
} from './constants.js';

// 拆分counterReducer
const initialCounterState = {
  counter: 0
}
function counterReducer(state = initialCounterState, action) {
  switch (action.type) {
    case ADD_NUMBER:
      return { ...state, counter: state.counter + action.num };
    case SUB_NUMBER:
      return { ...state, counter: state.counter - action.num };
    case INCREMENT:
      return { ...state, counter: state.counter + 1 };
    case DECREMENT:
      return { ...state, counter: state.counter - 1 };
    default:
      return state;
  }
}

export default counterReducer;

home/actionCreators.js

import axios from 'axios';

import {
  CHANGE_BANNERS,
  CHANGE_RECOMMEND,
  FETCH_HOME_MULTIDATA
} from './constants.js';


// 轮播图和推荐的action
export const changeBannersAction = (banners) => ({
  type: CHANGE_BANNERS,
  banners
});

export const changeRecommendAction = (recommends) => ({
  type: CHANGE_RECOMMEND,
  recommends
});


// redux-thunk中定义的action函数
export const getHomeMultidataAction = (dispatch, getState) => {
  axios({
    url: "http://123.207.32.32:8000/home/multidata",
  }).then(res => {
    const data = res.data.data;
    dispatch(changeBannersAction(data.banner.list));
    dispatch(changeRecommendAction(data.recommend.list));
  })
}


// redux-saga拦截的action
export const fetchHomeMultidataAction = {
  type: FETCH_HOME_MULTIDATA
}

home/constants.js

export const FETCH_HOME_MULTIDATA = "FETCH_HOME_MULTIDATA";
export const CHANGE_BANNERS = "CHANGE_BANNERS";
export const CHANGE_RECOMMEND = "CHANGE_RECOMMEND";

home/index.js

import reducer from './reducer';

export {
  reducer
}
//可以将home文件夹下所有的导出都集中到index.js中统一导出,更规范

home/reducer.js

import {
  CHANGE_BANNERS,
  CHANGE_RECOMMEND
} from './constants.js';

// 拆分homeReducer
const initialHomeState = {
  banners: [],
  recommends: []
}
function homeReducer(state = initialHomeState, action) {
  switch (action.type) {
    case CHANGE_BANNERS:
      return { ...state, banners: action.banners };
    case CHANGE_RECOMMEND:
      return { ...state, recommends: action.recommends };
    default:
      return state;
  }
}

export default homeReducer;

index.js/store

import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import createSagaMiddleware from "redux-saga";
import saga from "./saga";
import reducer from "./reducer.js";
// 1.引入thunk中间件
//  composeEnhancers函数--redux调试工具
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true }) || compose;

// const storeEnhancer = applyMiddleware(thunk);

// 2.创建sagaMiddleware中间件
const sagaMiddleware = createSagaMiddleware();

const storeEnhancer = applyMiddleware(thunk, sagaMiddleware);

const store = createStore(reducer, composeEnhancers(storeEnhancer));

sagaMiddleware.run(saga);

export default store;

reducer.js/store

import { reducer as counterReducer } from './counter';
import { reducer as homeReducer } from './home';

import { combineReducers } from 'redux';

// function reducer(state = {}, action) {
//   return {
//     counterInfo: counterReducer(state.counterInfo, action),
//     homeInfo: homeReducer(state.homeInfo, action)
//   }
// }

//reducer应该是一个什么类型? function
//合并
const reducer = combineReducers({
  counterInfo: counterReducer,
  homeInfo: homeReducer
});

export default reducer;

saga.js/store

import { takeEvery, put, all } from 'redux-saga/effects';
import axios from 'axios';
import {
  FETCH_HOME_MULTIDATA
} from './home/constants';
import {
  changeBannersAction,
  changeRecommendAction
} from './home/actionCreators';

function* fetchHomeMultidata(action) {
  const res = yield axios.get("http://123.207.32.32:8000/home/multidata");
  const banners = res.data.data.banner.list;
  const recommends = res.data.data.recommend.list;
  // yield put(changeBannersAction(banners));
  // yield put(changeRecommendAction(recommends));
  yield all([
    yield put(changeBannersAction(banners)),
    yield put(changeRecommendAction(recommends))
  ])
}

function* mySaga() {
  // takeLatest takeEvery区别:
  // takeLatest: 依次只能监听一个对应的action
  // takeEvery: 每一个都会被执行
  yield all([
    takeEvery(FETCH_HOME_MULTIDATA, fetchHomeMultidata),
    // takeLatest(ADD_NUMBER, fetchHomeMultidata),
  ]);
}

export default mySaga;

App.js

import React, { PureComponent } from "react";

import Home from "./pages/home";
import About from "./pages/about";
export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Home />
        <About />
      </div>
    );
  }
}

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import store from './store';
import { Provider } from 'react-redux';
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);