even

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

1、redux描述

Redux 是 JavaScript 状态容器,提供可预测化的状态管理, 学习网站

2、redux的基本使用

redux的三大原则: 单一数据源(只创建一个store)     state是只读的       使用纯函数来执行修改

安装

npm i redux --save

初始化redux

import {createStore} from 'redux'

let initState = {
    name: 'yfbill',
    age: 20
}

const reducer = (state = initState , action) => {
    switch(action.type) {
        case 'ADD': return {...state, age: ++state.age};  // 这里的...state表示里面原有的数据不能变, age对数据进行覆盖
        case 'MINUS': return {...state, age: --state.age};
        case 'CHANGE_NAME': return {...state, name: action.target}
        default: return {...state}
    }

}

export default createStore(reducer)  //初始化store实例

举例:

store有以下职责

  1. 维持应用的 state;

  2. 提供 getState() 方法获取 state;

  3. 提供 dispatch(action) 方法更新 state;

  4. 通过 subscribe(listener) 注册监听器;

  5. 通过 subscribe(listener) 返回的函数注销监听器。

import React, {Component} from 'react';
import ReactDom from 'react-dom'
import store from './store'

class App extends Component {
    componentDidMount() {
        this.fn = store.subscribe(() => {  //redux内部的观察者,当数据发生变化的时候就会触发对应的方法,该方法返回是取消监听的函数
            this.setState({}) //数据刷新后实现页面刷新
        })
    }
    componentWillUnmount() {
        this.fn()  //取消对应的事件监听
    }

    render() {
        return <div>
            <h2>this is App info</h2>
            <h3>name---{store.getState().name}</h3>
            <h3>age---{store.getState().age}</h3>
            <button onClick={() => {
                store.dispatch({type: 'ADD'})
            }}>age++</button>
        </div>
    }
}

ReactDom.render(<App/>, window.root)

 redux层的定义(这个就是redux生成store实例的方法)

import {createStore} from 'redux'

let initState = {
    name: 'yfbill',
    age: 20
}

const reducer = (state = initState , action) => {
    switch(action.type) {
        case 'ADD': return {...state, age: ++state.age};
        case 'MINUS': return {...state, age: --state.age};
        case 'CHANGE_NAME': return {...state, name: action.target}
        default: return {...state}
    }

}

export default createStore(reducer) //接收两个参数,一个是函数, 一个是中间件

因此通常公司中用redux进行开发,会以四个文件进行存放

  1. actionCreators.ts  存放dispatch中的函数封装
  2. constants.ts 存放action type的常量
  3. index.ts 导出creatorStore的总出口
  4. reducer.ts 存放reducer与初始化的state数据

3、react-redux的使用

为了实现react中更好的使用redux,可以使用依赖,react-redux实现两者更好的融合, 这个是把react与redux连接的依赖

安装react-redux

npm install react-redux

构建store依赖第二点所列的四个文件内容依次如下:

actionCreators.ts (存放dispatch中的函数封装)

import { ADD_COUNTER, REDUCE_COUNTER } from './constants';

// 递增state中的counter
export const addStateCounter = (payload: number) => ({
  type: ADD_COUNTER,
  payload,
});

// 递减state中的counter
export const reduceStateCounter = (payload: number) => ({
  type: REDUCE_COUNTER,
  payload,
});

constants.ts (存放action type的常量)

export const ADD_COUNTER = 'add_counter';

export const REDUCE_COUNTER = 'reduce_counter';

reducer.ts (存放初始化的state与reducer函数)

import { Reducer } from 'redux';
import { ADD_COUNTER, REDUCE_COUNTER } from './constants';

export interface IState {
  counter: number;
}

const initState: IState = {
  counter: 0,
};

export interface IAction {
  type: string;

  [key: string]: any;
}

export const reducer: Reducer<IState, IAction> = (
  state: IState = initState,
  action: IAction,
): IState => {
  switch (action.type) {
    case ADD_COUNTER:
      return { ...state, counter: state.counter + action.payload };

    case REDUCE_COUNTER:
      return { ...state, counter: state.counter - action.payload };

    default:
      return state;
  }
};

index.ts (导出store)

import { createStore } from 'redux';
import { reducer } from './reducer';

export default createStore(reducer);

react-redux provider的使用

import { Provider } from 'react-redux';
import { PureComponent, ReactElement } from 'react';

import store from './store';
import Content from './components/content';

class App extends PureComponent {
  public render(): ReactElement {
    return (
      <Provider store={store}>
        <h1>this is app</h1>
        <Content />
      </Provider>
    );
  }
}

export default App;

这里外层包Provider 并且注入store,这样子组件就方便进行接收

react-redux connect的使用

import { Dispatch } from 'redux';
import { connect } from 'react-redux';
import { PureComponent, ReactElement } from 'react';

import { IState } from '../store/reducer';
import { addStateCounter, reduceStateCounter } from '../store/actionCreators';

interface IContentProps {
  counter: number;

  addCounter: (num: number) => void;

  reduceCounter: (num: number) => void;
}

class Content extends PureComponent<IContentProps> {
  public render(): ReactElement {
    const { counter, addCounter, reduceCounter } = this.props;
    return (
      <div>
        <div>
          <span>counter: </span>
          <span>{counter}</span>
        </div>
        <div>
          <button onClick={() => addCounter(1)}>+1</button>
          <button onClick={() => reduceCounter(1)}>-1</button>
        </div>
      </div>
    );
  }
}

// 这个是把state注入到props
const mapStateToProps = (state: IState) => {
  return {
    counter: state.counter,
  };
};

// 这个是把方法注入的props
const mapDispatchToProps = (dispatch: Dispatch) => ({
  addCounter: (num: number) => dispatch(addStateCounter(num)),
  reduceCounter: (num: number) => dispatch(reduceStateCounter(num)),
});

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

4、多个reducer合并的场景

一个项目中,如果对模块进行划分,那么就会产生多个reducer,这个时候就需要对多个reducer进行合并

在第3点的基础上对代码进行调整,把目录结构调整成如下

 添加counter中的index.ts进行模块的导出(把IState改成ICounterState),具体代码如下:

export * from './actionCreators';

export type { ICounterState } from './reducer';

export { reducer } from './reducer';

在store/index.ts中代码进行如下调整

import { createStore, combineReducers } from 'redux';
import { reducer as counterReducer, ICounterState } from './modules/counter';


export interface ICombineState {
  counter: ICounterState;
}

const reducerCombine = combineReducers({
  counter: counterReducer,
});

export default createStore(reducerCombine);

并且把 connect处的state暴露部份进行调整

...
//
这个是把state注入到props const mapStateToProps = (state: ICombineState) => { return { counter: state.counter.counter, }; }; // 这个是把方法注入的props const mapDispatchToProps = (dispatch: Dispatch) => ({ addCounter: (num: number) => dispatch(addStateCounter(num)), reduceCounter: (num: number) => dispatch(reduceStateCounter(num)), }); export default connect(mapStateToProps, mapDispatchToProps)(Content);

5、 redux-thunk的使用

在react的日常开发中,通常会把请求服务写在redux中,但是在dispatch的时候,往往不接受函数,因此就需要使用中间伯redux-thunk来实现该功能(以下例子是对上面的例子进行改造)

在actionCreator中添加指令函数如下:

import { ThunkAction } from 'redux-thunk';
import { Dispatch, Action } from 'redux';
import { ADD_COUNTER, REDUCE_COUNTER, QUERY_USER_INFO } from './constants';
import { ICounterState } from './reducer';

...

const mockFunc = (): Promise<{ name: string; email: string }> => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        name: `even${Math.round(Math.random() * 10 + 1)}`,
        email: '457491559@qq.com',
      });
    }, 3000);
  });
};

export const queryUserInfo = (): ThunkAction<
  void,
  ICounterState,
  unknown,
  Action<string>
> => {
  return async (dispatch: Dispatch, state: () => ICounterState) => {
    const { name, email } = await mockFunc();

    console.log(state());

    dispatch({
      type: QUERY_USER_INFO,
      userInfo: { name, email },
    });
  };
};

在store出口处启用中间件注册

import { createStore, compose, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { reducer as counterReducer, ICounterState } from './modules/counter';


export interface ICombineState {
  counter: ICounterState;
}

const reducerCombine = combineReducers({
  counter: counterReducer,
});

export default createStore(
  reducerCombine,
  applyMiddleware(thunk)
);

这个时候就可以在调用层进行使用了

...
//
这个是把方法注入的props const mapDispatchToProps = (dispatch: Dispatch<any>) => ({ addCounter: (num: number) => dispatch(addStateCounter(num)), reduceCounter: (num: number) => dispatch(reduceStateCounter(num)), queryUserInfo: () => dispatch(queryUserInfo()), }); export default connect(mapStateToProps, mapDispatchToProps)(Content);

 6、react插件的使用

为了方便开发者进行开发和调试,可以安装如下两个插件

  • redux-devtools
  • react-devtools

在使用redux-devtools的时候需要在redux中进行配置,具体配置如下:

import { createStore, compose, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { reducer as counterReducer, ICounterState } from './modules/counter';

// const composeEnhancers =
//   (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ||
//   compose;

// 以下配置是相当于redux-devtools中的trace的配置打开 const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true }) ||
compose; export interface ICombineState { counter: ICounterState; } const reducerCombine = combineReducers({ counter: counterReducer, }); export default createStore( reducerCombine, composeEnhancers(applyMiddleware(thunk)), );

 

posted on 2021-04-06 23:28  even_blogs  阅读(324)  评论(0编辑  收藏  举报