为什么要使用状态管理
- 多个组件共用一个状态
- 状态管理,便于更新和维护
为什么React需要状态管理?
- react不是MVC框架,单纯可以看做是V(视图层框架),缺失了M和C,
- 在flux/redux/mobx中,react是他们的构成部分
redux的项目用法
- 工具
- redux
- react-redux 数据分块
- redux-thunk/redux-saga redux中的数据请求
- redux-devtools-extension 用于激活浏览器中redux扩展插件
- 安装一个redux chrome扩展插件: redux-devtools
- 构建四个部分
- store
- actionCreators/actions
- reducers
- View
redux基本了解
- redux是flux进阶版
- redux要求数据修改必须是纯函数
- redux的state是只读的,唯一的
- redux的state只能定义一个初始值
- 要求你拷贝
redux流程图
-
构成部分
- store 存储管理数据
- React Component 视图
- ActionCreators 动作创建者
- Reducers 派发器 用于修改数据,并且返回一个新的状态值
-
Flow
- React --> ActionCreators --> Reducers -> Store --state-> React
-
案例实现
redux_single_demo
- redux计数案例
flow:
1.首先打造redux中的 store
2.reducer.js 1)定义初始状态 ; 2)对初始状态进行拷贝,并将拷贝后的新状态return出去
3.组件中使用数据 1)组件中定义个状态来接受数据 ; 2)改数据的过程
4.actionCreators.js中定义方法 1)创建动作 ; 2)发送动作 - store来发
5.constant.js中定义动作的常量
6.reducer接受到store发送过来的动作,判断动作的类型,修改数据
7.useEffect进行视图的更新
src/store/index.js
import {createStore} from 'redux' //封装好了事件的订阅发布功能
import reducer from './reducer'
const store = createStore(reducer)
export default store
src/store/reducer.js
import {INCREMENT} from './constant'
//! 1. 定义初始状态
const initState = {
count: 0,
data: {
name: 'eason'
}
}
//reducer是一个纯函数 初始状态 动作
export default function reducer (state = initState,action) {
// const newState = Object.assign({},state)
// const newState = Object.create({},state)
const newState = JSON.parse(JSON.stringify(state))//redux的state是只读的,唯一的,要求你拷贝
// const newState = 递归深拷贝【自己封装、loadsh】
// const newState = 使用第三方的一个库【Immutable】
console.log('reducer')
switch (action.type) {
case INCREMENT:
newState.count ++
break;
default:
break;
}
return newState//返回一个新状态
}
src/store/constant.js
export const INCREMENT= 'INCREMENT'
src/store/actionCreators.js
import {INCREMENT} from './constant'
import store from './index'
export default {
//1.创建动作
increment () {
console.log('actions')
const action = {
type: INCREMENT
}
//2.发送动作 - store来发
store.dispatch(action)
}
}
App.js
import React,{useState,useEffect} from 'react'
import store from './store'
import actions from './store/actionCreators'
export default function App() {
const [count,setCount] = useState(store.getState().count)//redux自己封装的
function add () {
actions.increment()
}
useEffect(() => {
store.subscribe(() => {
setCount(store.getState().count)
})
},[])
return (
<div>
<button onClick={add}> + </button>
{count}
</div>
)
}
项目中的redux
- redux计数案例
src/store/index.js
import {createStore,applyMiddleware} from 'redux'//createStore封装好了事件的订阅发布功能
import thunk from 'redux-thunk'//中间件 处理redux异步的数据请求
import {composeWithDevTools} from 'redux-devtools-extension'//激活谷歌的拓展插件
import rootReducer from '../reducers'
/*
* rootReducer 项目中最大的reducer,用于管理项目中其他的reducer
* createStore(reducer,redux需要的中间件)
*/
export default createStore(rootReducer,composeWithDevTools(applyMiddleware(thunk)))
src/reducers/index.js
import {combineReducers} from 'redux'
import count from './count/reducer'
import list from './list/reducer'
export default combineReducers({
//里面存放的就是其他reducers
count,list
})
src/reducers/count/reducer.js
import {INCREMENT} from '../../constant'
//1.定义初始状态
const initState = {
n: 0
}
export default function reducer (state = initState,action) {
switch (action.type) {
case INCREMENT:
state.n++
return {...state}
break;
default:
return {...state}
break;
}
}
src/constant/index.js
export const INCREMENT = 'INCREMENT'
export const GET_LIST = 'GET_LIST'
src/actions/count/index.js
import {INCREMENT} from '../../constant'
export default {
increment () {
//! 这里没有发起数据请求
//! 这里我们只需要给一个返回值,这个返回值就是action
return {
type: INCREMENT
}
}
}
index.js - 入口文件
import store from './store'
//通过跨组件通信来获取数据
import {Provider} from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
src/pages/Count.js
import React from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import actions from '../actions/count'
function Count (props) {
const {n,increment} = props
return (
<div>
<button onClick={increment}> + </button>
<p> { n } </p>
</div>
)
}
/*
* 第一个参数是用于帮助我们获取store中的数据的
* 第二个参数是用于帮助我们获取actions中的方法,并将actions中创建的action发给reducer
*/
function mapStateFromProps (state) {
return {n: state.count.n}
}
function mapDispatchFromProps (dispatch) {
//actions就是把actions里的方法作成属性绑定在组件身上
//dispatch就是用dispatch来发送actions里的动作;actions里的动作全部都是actions文件中return的返回值
return bindActionCreators(actions,dispatch)
}
//connec函数的返回值是一个高阶组件
export default connect(mapStateFromProps,mapDispatchFromProps)(Count)
- redux数据请求案例
src/reducers/list/reducer.js
import {GET_LIST} from '../../constant'
//1.定义初始值
const initState = {
list: []
}
export default function reducer (state = initState,action) {
const newState = JSON.parse(JSON.stringify(state))
switch (action.type) {
case GET_LIST:
newState.list = action.payload//数据请求的返回值进行赋值
return newState
break;
default:
return newState
break;
}
}
src/actions/list/index.js
import {GET_LIST} from '../../constant'
export default {
getList ({reqType,cid}) {
//进行数据请求的
// return {type}
//! 异步数据请求这里返回的是一个以dispatch为参数的回调函数,因为异步数据请求很难写return的
return dispatch => {
// 1. 进行数据请求
// 2. 将数据请求结果作为action的一部分,发给reducer
fetch(`http://59.110.226.77:3000/api/list/${reqType}?cid=${cid}`)
.then(data => data.json())
.then(res => {
dispatch({
type: GET_LIST,
payload: res.data.content
})
})
.catch(error => Promise.reject(error))
}
}
}
src/pages/List.js
import React,{useEffect} from 'react'
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'
import actions from '../actions/list'
function List (props) {
const {list,getList} = props
useEffect(() => {
getList({
reqType: 'hot',
cid: 17
})
},[list])
if (list.length == 0) {
return <div> 数据为空 </div>
}
return (
<div>
<button onClick={() => { getList({reqType: 'hot',cid: 17 }) }}>人气</button>
<button onClick={() => { getList({reqType: 'price',cid: 17 }) }}>价格</button>
<button onClick={() => { getList({reqType: 'sell',cid: 17 }) }}>销量</button>
<button onClick={() => { getList({reqType: 'latest',cid: 17 }) }}>最新</button>
{list.map(item => <div key={item.id}> { item.d_title} </div>)}
</div>
)
}
export default connect(
state => state.list,
dispatch => bindActionCreators(actions,dispatch)
)(List)
文件夹目录