redux
redux 基本介绍
概念
redux是一个用来管理状态(数据)的框架。
为什么要用redux?
例如,一个组建的数据共享到其他组建,A1-1组建的数据要在A2和A2-1中使用,用以往的方式通过组建间传递也能实现,但是太过麻烦,如果项目较大,数据交互较多的场景,数据维护更为复杂,所以更适合将公共的数据放到公共的仓库中。
事实上,大多数情况,你可以不用它,只用 React 就够了。
曾经有人说过这样一句话。
“如果你不知道是否需要 Redux,那就是不需要它。”
Redux 的创造者 Dan Abramov 又补充了一句。
“只有遇到 React 实在解决不了的问题,你才需要 Redux 。”
简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。
- 用户的使用方式非常简单
- 用户之间没有协作
- 不需要与服务器大量交互,也没有使用 WebSocket
- 视图层(View)只从单一来源获取数据
上面这些情况,都不需要使用 Redux。
我不做大型项目,那么我还要去学习吗?
当然要学,现在不做大型项目,不代表以后不做大型项目,要为未来铺路,面试关于react技术栈,几乎必问redux,所以也为面试增砖添瓦。
准备工作
-
利用create-react-app快速初始化项目
create-react-app redux-demo
-
清理项目结构
-
安装redux
npm i redux
redux快速上手
创建stroe仓库
-
在src下创建store文件夹,在store下创建index.js
-
创建redux仓库
import { createStore } from 'redux' const defaultState = { count: 1 } const store = createStore((state = defaultState) => { return state }) export default store
getState
-
通过getState获取仓库的数据并展示
import React from 'react'; import store from './store' export default class App extends React.Component { constructor (props) { super(props) this.state = store.getState() } render () { return ( <div> { this.state.count } </div> ) } }
定义reducer
-
创建reducer.js文件
// 仓库数据 const defaultState = { count: 1 } const reducer = (state = defaultState) => { return state } export default reducer
-
在store/index.js中引入
import { createStore } from 'redux' import reducer from './reducer' const store = createStore(reducer) export default store
计数器案例
准备工作
-
准备Add组建
import React from 'react'; import store from './store' export default class Add extends React.Component { constructor (props) { super(props) this.state = store.getState() } render () { return ( <div> { this.state.count } <button>+1</button> </div> ) } }
-
准备sub组建
import React from 'react'; import store from './store' export default class Sub extends React.Component { constructor (props) { super(props) this.state = store.getState() } render () { return ( <div> { this.state.count } <button>-1</button> </div> ) } }
-
在App中使用
import React from 'react'; import Add from './Add' import Sub from './Sub' export default class App extends React.Component { render () { return ( <div> <Add /> <p>-----------</p> <Sub /> </div> ) } }
实现累加
错误🙅♂️写法
-
这样只修改了当前组建state,并不是修改了redux的store仓库的数据
addCount = () => { this.setState({ count: this.state.count + 1 }) } render () { return ( <div> { this.state.count } <button onClick={ this.addCount }>+1</button> </div> ) }
正确🙆♂️写法
-
add组建注册点击事件
render () { return ( <div> { this.state.count } <button onClick={ this.addCount }>+1</button> </div> ) }
-
通过dispatch触发修改state的操作,进行数据修改,参数是action
addCount = () => { store.dispatch({ type: 'addCount' }) }
-
在reducer纯函数中的第二个参数为action,action可以获取到dispatch操作的类型和数据
const reducer = (state = defaultState, action) => { // reducer 不允许直接修改 state的数据 if (action.type === 'addCount') { const newState = { ...state, count: state.count + 1 } return newState } return state }
安装redux-devtools
-
极简插件下载安装
-
配置
const store = createStore( reducer, /* preloadedState, */ + window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() );
实现数据更新
-
通过store.subscribe的回调监听数据发生变化
componentDidMount () { store.subscribe(this.storeChange) } storeChange = () => { this.setState({ ...store.getState() }) }
-
给sub组建同样添加
传递数据
-
传递数据
addCount = () => { store.dispatch({ type: 'addCount', value: 2 }) }
-
接收数据
// 仓库数据 const defaultState = { count: 1 } const reducer = (state = defaultState, action) => { // reducer 不允许直接修改 state的数据 if (action.type === 'addCount') { const newState = { ...state, count: state.count + action.value } return newState } return state } export default reducer
实现累减
-
注册事件
subCount = () => { store.dispatch({ type: 'subCount', value: 2 }) } render () { return ( <div> { this.state.count } <button onClick={this.subCount}>-1</button> </div> ) }
-
reducer中通过action处理
const reducer = (state = defaultState, action) => { // reducer 不允许直接修改 state的数据 if (action.type === 'addCount') { const newState = { ...state, count: state.count + action.value } return newState } if (action.type === 'subCount') { const newState = { ...state, count: state.count - action.value } return newState } return state }
redux工作流程
- 定义store数据仓库
- 通过store.getState()获取仓库数据
- 当组建视图发生更新通过dispatch创建action触发修改state的操作
- 在reducer中通过action获取修改操作和数据对store数据进行更新
- 组建内部通过通过store.subscribe的回调监听数据发生变化从而重新更新试图
优化action处理
因为在修改数据的时候,要通过store.dispath创建的type类型的值,和action中约定的值,从而实现数据修改,但是如果store.dispath创建的type类型的值或者action中约定的值写错,此时并不会报错,功能也不会实现,进而对调试有了难度。
-
创建actionType
const ADD_COUNT = 'ADD_COUNT' const SUB_COUNT = 'SUB_COUNT' export { ADD_COUNT, SUB_COUNT }
-
修改reducer.js
import { ADD_COUNT, SUB_COUNT } from './actionType' // 仓库数据 const defaultState = { count: 1 } const reducer = (state = defaultState, action) => { // reducer 不允许直接修改 state的数据 if (action.type === ADD_COUNT) { const newState = { ...state, count: state.count + action.value } return newState } if (action.type === SUB_COUNT) { const newState = { ...state, count: state.count - action.value } return newState } return state } export default reducer
-
修改Add组建
import React from 'react'; import store from './store' import { ADD_COUNT } from './store/actionType' export default class Add extends React.Component { constructor (props) { super(props) this.state = store.getState() } componentDidMount () { store.subscribe(this.storeChange) } storeChange = (e) => { console.log(123) this.setState({ ...store.getState() }) } addCount = () => { store.dispatch({ type: ADD_COUNT, value: 2 }) } render () { return ( <div> { this.state.count } <button onClick={ this.addCount }>+1</button> </div> ) } }
-
修改Sub组建
import React from 'react'; import store from './store' import { SUB_COUNT } from './store/actionType' export default class Sub extends React.Component { constructor (props) { super(props) this.state = store.getState() } componentDidMount () { store.subscribe(this.storeChange) } storeChange = () => { this.setState({ ...store.getState() }) } subCount = () => { store.dispatch({ type: SUB_COUNT, value: 2 }) } render () { return ( <div> { this.state.count } <button onClick={this.subCount}>-1</button> </div> ) } }
异步获取数据
通过axios获取数据
-
安装axios
yarn add axios
-
在Add组建中引入axios获取数据
import axios from 'axios' componentDidMount () { store.subscribe(this.storeChange) this.loadNewsList() } async loadNewsList () { const { data: res } = await axios.get('http://115.159.87.220:4000/books') console.log(res) }
将数据保存到store
-
定义type类型
const ADD_COUNT = 'ADD_COUNT' const SUB_COUNT = 'SUB_COUNT' const GET_NEWS = 'GET_NEWS' export { ADD_COUNT, SUB_COUNT, GET_NEWS }
-
引入GET_NEWS并通过dispatch触发action
import { ADD_COUNT, GET_NEWS } from './store/actionType' async loadNewsList () { const { data: res } = await axios.get('http://115.159.87.220:4000/books') console.log(res) store.dispatch({ type: GET_NEWS, value: res }) }
-
在reducer中修改数据
import { ADD_COUNT, SUB_COUNT, GET_NEWS } from './actionType' // 仓库数据 const defaultState = { count: 1, news: [] } const reducer = (state = defaultState, action) => { // reducer 不允许直接修改 state的数据 if (action.type === ADD_COUNT) { const newState = { ...state, count: state.count + action.value } return newState } if (action.type === SUB_COUNT) { const newState = { ...state, count: state.count - action.value } return newState } if (action.type === GET_NEWS) { const newState = { ...state, news: action.value } return newState } return state } export default reducer