React 生态之 Redux
Redux 初体验
通常会将 Redux 和 React 联系到一起,但是 Redux 不是寄生于 React 上,Redux 可以单独使用,也可以与 React 以外的第三方框架搭配使用。当然Redux 和 React 还是最好的搭配。Redux = Reducer + Flux ,这是 Redux 名字由来,所以不难看出 Redux 还是源于 Flux 而来,Flux 是为了 React 项目状态管理开发出来的。Redux 子所以备受欢迎,因为站在 Flux 肩膀上还回避掉了 Flux 的一些缺点。
现在 Redux 的作者 Dan 都被 Facebook 收了加入了 React 团队。所以 Redux 还是 React 对于状态说要选择。
今天我们可以脱离 React 先学习,学习 Redux
首先创建一个 React 项目,这就是运行 Redux 没有其他,你可以根据需要创建 vue 框架或者感觉 npm init -y 项目引入 rudex 然后 live-server 启动项目也是 OK,不过为了避免麻烦搭建脚手架,而且随后我们还是要回归到 React 上来分享 react-redux。
还是先分享一下 Redux 机制吧。
- 创建一个 Store ,在 store 里面有 state 状态也就是数据,store 有两个接口(方法) dispatch 和 subscribe ,分别是订阅和发布事件。因为 Redux 就是一个事件订阅和发布的系统。
- 在 Reducer ,这是一个纯函数,到底有多纯呢?就是接受 state(状态) 和 action(动作),接受两个参数就说明他还不是那么纯,应该是一个参数。reducer 负责根据 action 对 state 进行修改返回 state。
- dispatch 用于发送 action
-
sucribe 用于订阅 state 的改变后进行处理。
react-redux 机制
创建 store
引入 createStore 用于创建 store。
import { createStore } from 'redux';
定义 types 负责输出类型,所有事件(action)的类型都在这里定义,为了就是同样,为了避免不必要麻烦,也就是避免为人的录入错误。
export const FETCH_POSTS = 'FETCH_POSTS';
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const INCREMENT_ASYNC = 'INCREMENT_ASYNC'
导入类型文件,这里我们主要是简单的通过一个计数器来查看
创建 Reducer
import { INCREMENT, DECREMENT } from '../actions/types'
创建 reducer ,reducer 就是一个函数,reducer 接收两个参数 state 和 action,通过 action 更新 state 然后返回一个 state。
function counter(state = 0, action){
switch(action.type){
case INCREMENT:
return state + 1
case DECREMENT:
return state - 1
default:
return state;
}
}
- 这里 state 初始值给 0,也就是计数器的初始值为 0。
通过 createStore 方法来创建一个 store。
let store = createStore(counter);
订阅事件,store 的 getState 方法可以过去状态 state。
store.subscribe(()=> console.log(store.getState()))
派发事件
store.dispatch({type:INCREMENT})
store.dispatch({type:INCREMENT});
store.dispatch({type:DECREMENT});
let store = createStore(counter);
完整代码
import React, { Component } from 'react';
import { createStore } from 'redux';
import { INCREMENT, DECREMENT } from '../actions/types'
function counter(state = 0, action){
switch(action.type){
case INCREMENT:
return state + 1
case DECREMENT:
return state - 1
default:
return state;
}
}
let store = createStore(counter);
store.subscribe(()=> console.log(store.getState()))
store.dispatch({type:INCREMENT})
store.dispatch({type:INCREMENT});
store.dispatch({type:DECREMENT});
window.store = store;
export default class ReduxPage extends Component{
render(){
return(
<div>
<h1>Redux Demo</h1>
</div>
)
}
}
我们先看一看为什么 React 需要 Redux 呢?
export default class index extends Component {
constructor(){
super();
this.state = {
tut:"angularjs"
}
}
updateTut(newTut){
this.setState({
tut:newTut
});
}
render() {
return (
<div>
<Main changeTut={this.updateTut.bind(this)}/>
<Tut tut={this.state.tut}/>
</div>
)
}
}
上面代码 state 散落到 React 应用的各个角落不利于管理。Tut 接受 this.state.tut 然后 Main 由更新了 state
const Main = (props) => {
return (
<div>
<button
onClick={()=>props.changeTut('angular')}>
update angular
</button>
</div>
)
}
这样一来缺乏统一管理,对于小型应用我们可以凭借良好记忆,但是对于大型应用,这样势必是难于维护和扩展的。
其实从上面代码来看,redux 并没有那么复杂,就是一个对订阅和发布设计模式实现的框架。当然没有那么简单,但是无论他有多复杂其本质就是订阅和发布模式。
现在我们开始整合到 react 中,首先不使用 react-redux 直接。
先定义事件的类型
export const SET_NAME = "SET_NAME";
export const SET_TUT = "SET_TUT";
export const ADD = "ADD";
export const SUBTRACT = "SUBTRACT";
定义事件类型,通常是全部大写,单词用 _ 进行分割,以动词开头动词加名称的结构。
定义 Actions
import {SET_NAME,SET_TUT} from "../reducers/types";
export function setName(name){
return{
type:SET_NAME,
payload:name
}
}
export function setTut(tut){
return{
type:SET_TUT,
payload:tut
}
}
定义 Reducers
其实 reducer 也可以理解为一个规则定义器。制定 action 是如何更新 state 的规则。
import {SET_NAME,SET_TUT} from "./types"
const userReducer = (state = {
name:"zidea",
tuts:50
}, action) => {
switch (action.type) {
case SET_NAME:
state = {
...state,
name:action.payload
}
break;
case SET_TUT:
state = {
...state,
tuts:action.payload
}
break;
}
return state;
}
export default userReducer
创建 store 对象
import { createStore,combineReducers,applyMiddleware } from 'redux';
import logger from 'redux-logger'
import math from '../reducers/mathReducer';
import user from '../reducers/userReducer'
import thunk from 'redux-thunk';
import promise from 'redux-promise';
const mLogger = (store) => (next) => (action) => {
console.log("Logged Action: ", action);
next(action)
}
const store = createStore(combineReducers(
{
math,
user
}),
{},
applyMiddleware(mLogger,logger,thunk,promise));
export default store;
- 这里顺便说一下中间件
中间件事位于 action 和 reducer 之间,在传递 action 同时进行一些操作,如果 next(action) 向下传递 action 动作将不会被执行。
将 store 与应用关联
import AppWithRedux from './AppWithRedux'
import {Provider} from 'react-redux';
import store from './AppWithRedux/store'
function App() {
return (
<Provider store={store}>
<div className="App">
{/* <AdvApp/> */}
<AppWithRedux/>
</div>
</Provider>
);
}
export default App;
-通过 react-redux 提供 Provider 可以将 store 共享到整个应用中去。
重写组件,组件与 Redux 关联
import React, { Component } from 'react'
import Tut from '../AppWithoutRedux/components/Tut';
import Main from '../AppWithoutRedux/components/Main';
import {Provider,connect} from 'react-redux'
import {setName} from "./actions/userActions";
class index extends Component {
constructor(){
super();
}
updateTut(newTut){
}
render() {
return (
<div>
<h1>React 和 Redux</h1>
<Main changeTut={() => this.props.setName('tina')}/>
<Tut tut={this.props.user.name}/>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
user:state.user,
math:state.math
}
}
const mapDispatchToProps = (dispatch) => {
return{
setName:(name)=>{
dispatch(setName(name))
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(index)
从 react-redux 引入 connect 负责将组件和 redux 相关联。关联接受两个 mapStateToProps 和 mapDispatchToProps。当然可以根据自己喜好起其他名称,
- mapStateToProps: 我们知道每一个 reducer 都有一个 state ,mapStateToProps 负责将 reducer 中 state 某个属性对应到 props 上属性。
- mapDispatchToProps 传入 dispatch 返回一个以对象这个对象里方法会添加 props 从而达到将 props 发放可以发送更新 state 的事件。
现在问题是 store 的订阅是在哪里做的呢?也就是 subscribe 方法怎么实现的。
无需 redux-react 实现 redux 管理组件状态
import {Provider,connect} from 'react-redux'
constructor(props){
super(props);
this.state = { counter: store.getState()}
store.subscribe(()=> this.setState({
counter:store.getState()
}))
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
- 将 store 的 state 和组件的 state 进行关联
- 在构造函数中订阅 store 的事件,根据事件更新组件 state
increment(){
store.dispatch({type:INCREMENT});
}
decrement(){
store.dispatch({type:DECREMENT});
}
- 在动作中发布事件,这样大家就清晰 redux-react 的实现
export default class ReduxPage extends Component{
constructor(props){
super(props);
this.state = { counter: store.getState()}
store.subscribe(()=> this.setState({
counter:store.getState()
}))
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
increment(){
store.dispatch({type:INCREMENT});
}
decrement(){
store.dispatch({type:DECREMENT});
}
render(){
return(
<div>
<h1>Redux Demo</h1>
<p>{this.state.counter}</p>
<div>
<button
onClick={this.increment}
>increment</button>
<button
onClick={this.decrement}
>decrement</button>
</div>
</div>
)
}
}
我们将 redux 从项目抽离出来,作为组件单独来使用,store 作为组件 props 属性传入到组件
return(
<div>
<h1>Redux Demo</h1>
<Counter store={store}/>
</div>
)
定义组件,在组件构造函数里处理订阅事件。
constructor(props){
super(props);
console.log(props)
this.state = {
counter: props.store.getState()
}
props.store.subscribe(()=> this.setState({
counter:props.store.getState()
}))
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
应用端完整代码
import React, { Component } from 'react';
import { createStore } from 'redux';
import { INCREMENT, DECREMENT } from '../actions/types'
import Counter from '../components/Counter';
function counter(state = 0, action){
switch(action.type){
case INCREMENT:
return state + 1
case DECREMENT:
return state - 1
default:
return state;
}
}
let store = createStore(counter);
export default class ReduxPage extends Component{
constructor(props){
super(props);
}
render(){
return(
<div>
<h1>Redux Demo</h1>
<Counter store={store}/>
</div>
)
}
}
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { INCREMENT, DECREMENT } from '../actions/types'
class Counter extends Component {
constructor(props){
super(props);
console.log(props)
this.state = {
counter: props.store.getState()
}
props.store.subscribe(()=> this.setState({
counter:props.store.getState()
}))
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
increment(){
this.props.store.dispatch({type:INCREMENT});
}
decrement(){
this.props.store.dispatch({type:DECREMENT});
}
render(){
return(
<div>
<p>{this.state.counter}</p>
<div>
<button
onClick={this.increment}
>increment</button>
<button
onClick={this.decrement}
>decrement</button>
</div>
</div>
)
}
}
export default Counter;
希望我的分享对大家有所帮助。
react 生态