写在前面的话:
redux 可结合react等库一起使用,下面先介绍单独使用redux时的用法。
用法一
让我们结合一个计数器案例来看它的使用吧 😊
假设页面上分别由加、减两个button 和 一个span标签(如下), 我们想通过点击button,实现count的加减。
<button id = 'plus'> + </button>
<span id='count'> 0 </span>
<button id = 'minus'> - </button>
一. 引入cdn版本
<script src='https://cdn.bootcdn.net/ajax/libs/redux/4.1.0/redux.js'></script>
这样就在全局得到一个Redux对象
1. 创建store
const store = Redux.createStore(reducer)
3. 定义初始状态
const intitalState = {
count: 0,
}
2. 定义reducer
const reducer = (state = intitalState, action)=> {
switch (action.type){
case "increment":
return {
count: state.count +1
};
case "decrement":
return {
count: state.count - 1
};
default:
return state
}
}
4. 定义action
const increment = {
type: "increment"
}
const decrement = {
type: "decrement"
}
5. 触发action
const plus = document.getElementById("plus")
const minus = document.getElementById("minus")
plus.addEventListener('click', () => {
store.dispatch(increment)
})
minus.addEventListener('click',() => {
store.dispatch(decrement)
})
6. 订阅store,状态发生改变时手动更新
store.subscribe(() => {
const el = document.getElementById("count");
el.innerHTML = store.getState().count
})
总结 redux的核心api
//1. 创建Store容器
const store = Redux.createStore(reducer)
//2. 创建处理状态的reducer
function reducer (state, action) {
// 函数的返回值即为store对象
// reducer 函数接收两个参数
// state 即为向store中存储的状态,(可通过createStore的第二个参数指定,也可以通过函数默认参数的形式制定)
// 即为触发的action对象,被reducer的第二个参数接收。 可根据action中type值的不同,对store中的状态进行处理
}
//3. 获取状态
store.getState()
//4. 订阅状态
store.subscribe(function () {
//当store中的状态发生变化时,store就会执行subscribe中传递的函数
//通常使用这个方法得到store的最新状态以更新视图
})
//5. 触发action
store.dispatch({type: 'xxxx'}) //通过调用dispatch 触发 action
用法二. redux 在react中的使用
首先看下图,了解redux 真正的工作流程:
接下来,我们结合react-redux 一步步来改造下上面的案例。
1.安装项目依赖
- yarn add redux react-redux
2.代码替换
然后在index.js中定义好Counter组件,替换掉App组件。
并把我们之前写的代码搬过来
如下:
import { createStore } from 'redux'
const intitalState = {
count: 0,
}
const reducer = (state = intitalState, actions) => {
switch (actions.type) {
case "increment":
console.log("increment")
return {
count: state.count +1
};
case "decrement":
console.log("increment")
return {
count: state.count - 1
};
default:
return state
}
}
const store = createStore(reducer);
console.log(store.getState(), "state");
const increment = { type: 'increment' };
const decrement = { type: 'decrement' };
function Counter () {
return (
<div>
<button id = 'plus' onClick = {() => store.dispatch(increment)}>
+
</button>
<span id='container'>
0
</span>
<button id = 'minus' onClick = {() => store.dispatch(decrement)}>
-
</button>
</div>
)}
store.subscribe(() => {
console.log(store.getState())
})
ReactDOM.render(
<React.StrictMode>
{/* <App /> */}
<Counter/>
</React.StrictMode>,
document.getElementById('root')
);
3.调试看页面展示效果
切到浏览器,尝试点击button,控制台中可以看到如下结果:
由此可见,我们确实能成功的更改store中的状态。
so,当状态发生改变时,我们应该同步给视图。
即,当状态发生改变时,我们需要重新去渲染下组件。
那么,我们这样来做:
function Counter () {
return <div>
<button id = 'plus' onClick = {() => store.dispatch(increment)}>
+
</button>
<span id='container'>
{/* 组件中获取store的最新状态 */}
{store.getState().count}
</span>
<button id = 'minus' onClick = {() => store.dispatch(decrement)}>
-
</button>
</div>
}
store.subscribe(() => { // store中的状态发生改变时,我们再次去渲染Counter组件
ReactDOM.render(
<React.StrictMode>
<Counter/>
</React.StrictMode>,
document.getElementById('root')
);
})
ReactDOM.render( //初识渲染组件
<React.StrictMode>
{/* <App /> */}
<Counter/>
</React.StrictMode>,
document.getElementById('root')
);
总结
我们可以看到视图确实更新了。
但却存在很多问题,只渲染个Counter组件,我们就写了两遍,并在其他组件里我们也没办法拿到store对象等。
下一节,我们就一起来看下react 和 redux是如何结合在一起的。
用法三: react-redux 与 redux的结合
前置知识:
react-redux里包含:
1. Provider组件:
作用:把我们创建的store 放在全局,各个组件都能够得到的地方。 <br/>
用法: Provider组件要包裹项目中所有的组件,即应该放在最外层组件上
2. connect方法:
作用:
1. 帮助我们订阅store,当store中的状态发生改变时,会帮我们重新渲染组件。
2. 帮助我们拿到store中的状态,将store中的状态通过组件的props属性映射给组件。
3. 通过connect方法,我们可以获取dispatch方法,通过dispatch方法来调用action。
使用:<br/>
调用connect方法会在返回一个方法,我们需要调用返回的方法,并把组件作为参数传递进去。<br/>
(因为当store的状态发生变化时,需要知道要更新哪个组件,也需要知道store中的状态要映射给哪个组件)
🌰
import { connect } form 'react-redux';
export default connect()(Counter)
获取store中的状态并映射到组件的props中:
connect方法的调用有一个形参,是个函数,这个函数接收一个形参,即为store中的state
🌰
const mapStateToProps = state => {//返回一个对象,返回的对象会映射到组件的pros中
return {
count: state.count
}
}
export default connect(mapStateToProps)(Counter)
3. connect方法的第二个参数:
将dispatch映射到组件的props中,在组件中就可以通过调用props.xxx来触发action
🌰
const mapDispatchToProps = dispatch => {//返回一个对象,返回的对象同样会映射到组件的pros中
return {
increment(){
dispatch({type: 'increment'})
},
decrement(){
dispatch({type: 'decrement'})
}
}
}
export default connect(mapStateToProps, mapActionToProps)(Counter)
4. bindActionsCreators
仔细观察上面代码中的increment,decrement方法都调用了duspatch方法,结构也一致。所以我们想要简化代码,redux提供给了一个 bindActionsCreators方法来做这件事情,帮助我们生成我们想要的函数
🌰
// bindActionCreators的返回值即为你传入的方法。
const mapDispatchToProps = (dispatch) => bindActionCreators({
increment(){
return { type: 'increment' }
},
decrement(){
return { type: 'decrement' }
}
}, dispatch)
5. action 传参
我们在组件调用action地方携带参数,
然后store模块中定义action的函数里会默认接受到我们传递的参数
然后在reducer中就可以通过action.payload拿到我们传递的参数,来做相应的业务逻辑
🌰
<button id='plus' onClick = {()=> increment(5)}> + </button>
// actions
const increment = (payload) => ({ type: INCREMENT, payload })
//reducer
const reducer = (state, action) => {
switch(action.type){
case 'increment'
return {
count: state.count + action.payload
}
......
}
}
以上是基本使用,我们再一起回顾并完善之前的代码吧
最后:
代码重构 & store各模块的抽离
// src/store/index.js
import { createStore } from 'redux';
import reducer from './reducers/counter.reducer';
const store = createStore(reducer)
// src/store/reducers/counter.reducer.js
import { INCREMENT, DECREMENT } from "../const/count.count";
const intitalState = {
count: 0,
}
export default (state = intitalState, action) => {
switch (actions.type) {
case INCREMENT:
return {
count: state.count + action.payload
};
case DECREMENT:
return {
count: state.count - action.payload
};
default:
return state
}
}
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { store } from './store';
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
// src/App
import Counter from "./components/Counter";
function App() {
return (
<div className="App">
<Counter/>
</div>
);
}
export default App;
//src/store/actions/counter.actions.js
import { INCREMENT, DECREMENT } from "../const/count.count"
export const increment = (payload) => ({ type: INCREMENT, payload })
export const decrement = (payload) => ({ type: DECREMENT, payload })
// src/components/Counter
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as counterActions from '../store/actions/counter.actions'
function Counter ({count,increment, decrement}) {
return (
<div>
<button id = 'plus' onClick = {()=> increment(5)}>
+
</button>
<span id='container'>
{count}
</span>
<button id = 'minus' onClick = {()=> decrement(5)}>
-
</button>
</div>)
}
const mapStateToProps = (state) => ({
count: state.count
});
const mapDispatchToProps = (dispatch) => bindActionCreators(counterActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Counter)
其他Api
当然redux中还有其他帮助我们减少代码模版的方法,比如combineReducer(用来合并reducer)等。