Redux、Redux-Thunk、Redux-Sagas、React-Redux学习笔记

Redux、Redux-Thunk、Redux-Sagas、React-Redux学习笔记

React环境配置

  • 安装脚手架工具
npm install -g create-react-app
  • 创建项目工程
// demo是项目名称,不能有大写字母
create-react-app demo
  • 安装浏览器调试工具

在谷歌扩展程序中安装 React Developer Tools 和 Redux DevTools
React Developer Tools能够在工具栏中很方便的看到页面的布局
Redux DevTools能够追踪页面state、action等的变化

Redux

  • Redux作用

当项目越来越复杂时,组件越来越多的时候,组件之间数据的共享就成为了一个问题,那么Redux就是用来解决各个组件之间数据共享的问题的。

  • 在项目中安装Redux
redux的GitHub地址:https://github.com/reduxjs/redux

// 两种安装方法
npm install --save redux
yarn add redux
  • 理解Redux的工作流程

Redux、Redux-Thunk、Redux-Sagas、React-Redux学习笔记_第1张图片

1、Store就是项目中的数据仓库,但是数据是交给Reducer管理的
2、Store向组件提供数据,组件订阅数据后,会根据状态的变化自动更新数据
3、组件要修改Store中的数据,需要创建一个action利用dispatch函数通知Store
4、Stroe把action和数据交给Reducer,Reducer根据action的类型来处理数据
5、Reducer把处理好的数据返回给Store

  • 从Store中读取数据
// 创建reducer管理数据 
// 默认数据
const defaultState = {
    inputValue: 'zhangsan'
} 

export default (state = defaultState, action) => {
    return state;
}

// 创建store数据仓库
import { createStore } from 'redux';
import reducer from './reducer';

// window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 浏览器插件调试支持redux
const store = createStore(
    reducer, 
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 
);
export default store;


// 创建一个简单的页面,从store中读取默认数据
class App extends Component {
    constructor(props) {
        super(props);
        // 将store中的数据放到该组件的state中
        this.state = store.getState();
    }

    render() {
        return (
            <div>
                "请输入信息"
                    style={{width: "200px", height: '40px', border: '1px solid red'}}
                    value={this.state.inputValue}
                    onChange={this.handleChangeInput}
                />
            div>
        );
    }

    // 等下派发action改变store中的数据
    handleChangeInput() {
        console.log('ss');
    }
}

export default App;

效果展示:
Redux、Redux-Thunk、Redux-Sagas、React-Redux学习笔记_第2张图片

  • 派发action改变Store中的数据
// 在App组件中input控件中输入内容时派发action
handleChangeInput() {
    const action = {
        type: 'change_input_value',
        value: e.target.value
    }
    store.dispatch(action);
}

// 在Reducer中接收处理action,改变inputValue的值,返回新数据给Store
export default (state = defaultState, action) => {
    if (action.type === 'change_input_value') {
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value;
        return newState;
    }
    return state;
}

// 此时页面输入框的值仍然不会改变,但通过redux调试工具可以看到state已经改变了
// App组件中还需监听store的变化
constructor(props) {
    super(props);
    this.state = store.getState();
    this.handleChangeInput = this.handleChangeInput.bind(this);
    this.handleStoreChange = this.handleStoreChange.bind(this);
    // 订阅
    store.subscribe(this.handleStoreChange);
}

// 当store中的数据发生变化时会调用这个方法,重新将新的值赋给App组件的state,从而改变页面
handleStoreChange () {
    this.setState(store.getState());
}
  • 代码优化

在上面的代码中,action是一个字符串,在App组件、reducer文件中都使用了,容易引起难以调试的Bug,也不利于后期维护,下面通过一个文件来管理action的类型和action的创建。代码如下:

// App组件
import React, { Component } from 'react';
import store from './store';
import { changeInputValue } from './store/actionCreators';

class App extends Component {
    constructor(props) {
        super(props);
        this.state = store.getState();
        this.handleChangeInput = this.handleChangeInput.bind(this);
        this.handleStoreChange = this.handleStoreChange.bind(this);
        store.subscribe(this.handleStoreChange);
    }

    render() {
        return (
            
"请输入信息" style={{width: "200px", height: '40px', border: '1px solid red'}} value={this.state.inputValue} onChange={this.handleChangeInput} />
); } handleChangeInput (e) { store.dispatch(changeInputValue(e.target.value)); } handleStoreChange () { this.setState(store.getState()); } } export default App; ------------------------------ // actionTypes.js 管理action类型 export const CHANGE_INPUT_VALUE = 'change_input_value'; ------------------------------ // actionCreators.js 管理action的创建 import { CHANGE_INPUT_VALUE } from './actionTypes'; export const changeInputValue = (value) => ({ type: CHANGE_INPUT_VALUE, value: value }) ------------------------------ // reducer.js import { CHANGE_INPUT_VALUE } from './actionTypes'; const defaultState = { inputValue: 'zhangsan' } export default (state = defaultState, action) => { if (action.type === CHANGE_INPUT_VALUE) { const newState = JSON.parse(JSON.stringify(state)); newState.inputValue = action.value; return newState; } return state; } ------------------------------ // store/index.js import { createStore } from 'redux'; import reducer from './reducer'; const store = createStore( reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ); export default store;

Redux-Thunk

  • Redux-Thunk的作用

Redux-Thunk是Redux的中间件,用来将组件异步获取数据的操作封装到action中去,以此来减少组件中复杂的异步操作。使用Redux-Thunk之后action可以返回一个函数(Redux的action默认只能返回对象)。

  • 安装以及配置
GitHub地址: https://github.com/reduxjs/redux-thunk
yarn add redux-thunk
npm install --save redux-thunk // 或者

// 参考 GitHub: https://github.com/zalmoxisus/redux-devtools-extension
  • 使用Redux-Thunk之后的数据流程

Redux、Redux-Thunk、Redux-Sagas、React-Redux学习笔记_第3张图片

实际上变化就在Dispatch中,使用Redux-Thunk之后action不仅可以返回对象,还可以返回函数,然后将异步操作代码放在action中了。

  • 代码演示

这里采用从服务端获取数据展示一个ul列表

只使用Redux的代码

// App 组件
import React, { Component } from 'react';
import store from './store';
import { getListData } from './store/actionCreators';
import axios from 'axios';

class App extends Component {

    constructor(props) {
        super(props);
        this.state = store.getState();
        this.handleStoreChange = this.handleStoreChange.bind(this);
        store.subscribe(this.handleStoreChange);
    }

    render() {
        return (
            
    {this.state.list.map((item, index) => { return
  • {item}
  • })
    } ul
    > div> ); } componentDidMount() { // 异步获取数据 axios.get('/list.json').then((res) => { if (res.data) { store.dispatch(getListData(res.data)); } }).catch((e) => { console.log(e); }) } handleStoreChange () { this.setState(store.getState()); } } export default App; ----------------------- // actionTypes.js export const GET_LIST_DATA = 'get_list_data'; ----------------------- // actionCreators.js import { GET_LIST_DATA } from './actionTypes'; export const getListData = (data) => ({ type: GET_LIST_DATA, value: data }) ----------------------- // reducer.js import { GET_LIST_DATA } from './actionTypes'; const defaultState = { list: [] } export default (state = defaultState, action) => { if (action.type === GET_LIST_DATA) { const newState = JSON.parse(JSON.stringify(state)); newState.list = action.value; console.log(action); return newState; } return state; } ----------------------- // store.js import { createStore } from 'redux'; import reducer from './reducer'; const store = createStore( reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ); export default store;

使用Redux-Thunk之后的代码以及配置

// App.js
import React, { Component } from 'react';
import store from './store';
import { getListDataAction } from './store/actionCreators';

class App extends Component {

    constructor(props) {
        super(props);
        this.state = store.getState();
        this.handleStoreChange = this.handleStoreChange.bind(this);
        store.subscribe(this.handleStoreChange);
    }

    render() {
        return (
            
    {this.state.list.map((item, index) => { return
  • {item}
  • })
    } ul
    > div> ); } componentDidMount() { // 着重变化的代码 store.dispatch(getListDataAction()) } handleStoreChange () { this.setState(store.getState()); } } export default App; ----------------------- // actionCreators.js import { GET_LIST_DATA } from './actionTypes'; import axios from 'axios'; export const getListData = (data) => ({ type: GET_LIST_DATA, value: data }) // action 返回一个函数,异步操作在这里进行 export const getListDataAction = () => { return (dispatch) => { axios.get('/list.json').then((res) => { if (res.data) { dispatch(getListData(res.data)); } }).catch((e) => { console.log(e); }) } } ----------------------- // store.js import { createStore, applyMiddleware, compose } from 'redux'; import thunk from 'redux-thunk'; import reducer from './reducer'; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose; const enhancer = composeEnhancers( applyMiddleware(thunk) ); const store = createStore( reducer, enhancer ); export default store; // reducer.js actionTypes.js 代码没有变化

演示效果:
Redux、Redux-Thunk、Redux-Sagas、React-Redux学习笔记_第4张图片

redux-saga

  • 作用

redux-saga同样是用来拆分组件异步代码的中间件,它和Redux-Thunk的区别就是将异步代码的操作放到一个单独的文件中去管理。

  • 安装
GitHub:https://github.com/redux-saga/redux-saga
npm install --save redux-saga
yarn add redux-saga
  • 代码演示

这里采用上面的例子,同样从服务器获取数据展示在ul列表中。

// App.js
import React, { Component } from 'react';
import store from './store';
import { getListDataSagas } from './store/actionCreators';

class App extends Component {

    constructor(props) {
        super(props);
        this.state = store.getState();
        this.handleStoreChange = this.handleStoreChange.bind(this);
        store.subscribe(this.handleStoreChange);
    }

    render() {
        return (
            
    {this.state.list.map((item, index) => { return
  • {item}
  • })
    } ul
    > div> ); } componentDidMount() { store.dispatch(getListDataSagas()); } handleStoreChange () { this.setState(store.getState()); } } export default App; ------------------------ // actionTypes.js export const GET_LIST_DATA = 'get_list_data'; export const GET_LIST_DATA_SAGAS = 'get_list_data_sagas'; ------------------------ // actionCreators.js import { GET_LIST_DATA, GET_LIST_DATA_SAGAS } from './actionTypes'; export const getListData = (data) => ({ type: GET_LIST_DATA, value: data }) // 这里创建了一个sagas中需要的action export const getListDataSagas = () => ({ type: GET_LIST_DATA_SAGAS, }) ------------------------ // reducer.js import { GET_LIST_DATA } from './actionTypes'; const defaultState = { list: [] } export default (state = defaultState, action) => { if (action.type === GET_LIST_DATA) { const newState = JSON.parse(JSON.stringify(state)); newState.list = action.value; console.log(action); return newState; } return state; } ------------------------ // store.js import { createStore, applyMiddleware, compose } from 'redux'; import reducer from './reducer'; import createSagaMiddleware from 'redux-saga'; // 新创建的sagas.js文件用来管理一步操作的代码 import sagas from './sagas'; const sagaMiddleware = createSagaMiddleware(); const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose; const enhancer = composeEnhancers( applyMiddleware(sagaMiddleware) ); const store = createStore( reducer, enhancer ); sagaMiddleware.run(sagas); export default store; ------------------------ // sagas.js 单独管理异步操作的文件 import { put, takeEvery } from 'redux-saga/effects'; import { GET_LIST_DATA_SAGAS } from './actionTypes'; import axios from 'axios'; import { getListData } from './actionCreators'; function* mySaga() { // 这句代码意思是:当接收到一个名叫GET_LIST_DATA_SAGAS的action的时候回去调用getDataFromServer方法 yield takeEvery(GET_LIST_DATA_SAGAS, getDataFromServer); } // 获取数据的异步操作 function* getDataFromServer() { try { const res = yield axios.get('/list.json'); yield put(getListData(res.data)); } catch (e) { console.log('请求错误'); } } export default mySaga;

React-Redux

  • 作用

React-Redux的作用是为了在项目中使用Redux更加方便。

  • 安装
GitHub地址:https://github.com/reduxjs/react-redux
npm install --save react-redux
yarn add react-redux 
  • 代码演示

代码演示的例子是改变input输入框中的内容

// index.js 项目的入口文件
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from  'react-redux';
import store from  './store';

const AppRoot = (
    // 重点把store提供给每一个组件
  
      
  
);
ReactDOM.render(AppRoot, document.getElementById('root'));

------------------------
// actiionTypes.js 
export const INPUT_CHANGE = 'input_change';

------------------------
// actionCreators.js
import { INPUT_CHANGE } from './actionTypes';

export const inputChange = (data) => ({
    type: INPUT_CHANGE,
    value: data
}) 

------------------------
// store.js
import { INPUT_CHANGE } from './actionTypes';
const defaultState = {
    inputValue: ''
}
export default (state = defaultState, action) => {
    if (action.type === INPUT_CHANGE) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value;
        return newState;
    }
    return state;
}

------------------------
// App.js
import React, { Component } from 'react';
import { inputChange } from './store/actionCreators';
import { connect } from 'react-redux';

class App extends Component {
    render() {
        return (
            
"请输入信息" style={{width: "200px", height: '40px', border: '1px solid red'}} value={this.props.inputValue} onChange={this.props.handleChangeInput} />
); } } // 将store中存储的数据映射到当前组件的props中 const mapStateToProps = (state) => { return { inputValue: state.inputValue } } const mapDispatchToProps = (dispatch) => { return { handleChangeInput (e) { dispatch(inputChange(e.target.value)); } } } // 利用connect将组件和store连接(连接规则mapStateToProps、mapDispatchToProps) export default connect(mapStateToProps, mapDispatchToProps)(App);

项目基础骨架

通过上面一系列的讲解,对React有了一定的认识,下面通过Rudux、React-Redux搭建一个项目的基础骨架。

  • 创建项目
create-react-app baseproject
  • 安装依赖
yarn add redux
yarn add react-redux
  • 代码演示

react推崇的是组件化开发,下面就创建一个有两个组件的demo演示基本骨架的搭建。
Redux、Redux-Thunk、Redux-Sagas、React-Redux学习笔记_第5张图片

// App.js 是项目的根组件
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import store from './store';
import ComponentA from './ComponentA/ComponentA';
import ComponentB from './ComponentB/ComponentB';

class App extends Component {
  render() {
    return (
      
        
搭建项目基础骨架
); } } export default App; ------------------------------ // store文件夹是项目数据的总仓库,下面分别介绍每个文件 // index.js 数据仓库的总配置 import { createStore } from 'redux'; import reducer from './reducer'; const store = createStore( reducer, /* preloadedState, */ window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ); export default store; ------------------------------ // reducer.js 管理整个项目的数据 // 这里创建了两个子组件的reducer来管理各自的数据 import { reducer as commonentAReducer } from '../ComponentA/store'; import { reducer as commonentBReducer } from '../ComponentB/store'; import { combineReducers } from 'redux'; const reducer = combineReducers({ componentA: commonentAReducer, componentB: commonentBReducer }) export default reducer; ------------------------------ // 子组件目录介绍 // CommonentA.js 子组件 store 子组件数据仓库 import React, { Component } from 'react'; import { connect } from 'react-redux'; class ComponentA extends Component { render() { return (
{this.props.testValue}
); } } const mapStateToProps = (state) => { return { testValue: state.componentA.testValue } } export default connect(mapStateToProps,null)(ComponentA); ------------------------------ // actionTypes.js 管理action类型的文件 // actionCreators.js 管理action的创建的文件 ------------------------------ // reducer.js 子组件存储数据处理action逻辑的文件 import * as actionTypes from './actionTypes'; const defaultValue = { testValue: '组件A中的默认测试数据' } export default (state = defaultValue, action) => { return state; }; ------------------------------ // index.js 作为子组件提供外界引用的接口文件,外界只需要引入这个文件就可以使用该组件中的内容 import reducer from './reducer'; import * as actionCreators from './actionCreators'; import * as actionTypes from './actionTypes'; export { reducer, actionCreators, actionTypes }

演示效果:
Redux、Redux-Thunk、Redux-Sagas、React-Redux学习笔记_第6张图片

总结

Redux: 就是用来管理项目中状态或者数据的
Redux-Thunk: Redux的中间件,用来将异步操作的代码拆分到action中去的
Redux-Sagas: Redux的中间件,用来将异步操作的代码拆分到单独的文件中管理
React-Redux: 更能方便的管理和使用Redux

你可能感兴趣的:(前端)