redux学习之demo

本文介绍了使用Redux构建React项目的步骤,包括初始化项目、创建Store、整合reducers、使用redux-thunk中间件处理异步操作、注入Store到组件以及组件如何通过connect连接Redux。通过这个过程,读者可以理解Redux在实际应用中的工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、demo 解析

2、新建一个react项目

3、项目大概这个样子

4、先给自己建立一个Store库,这就是你redux数据的仓库了

store文件夹下有两个文件,

(1)reducers,把你各个页面的reducer汇合起来,给他们起不同的好听的名字,

         我这里只有一个home页面

import { combineReducers } from 'redux'

import home from 'pages/home/reducer'

export default combineReducers({
    home
})

(2)另一个文件是index.js

         主要是用来创建你的库,创建库的时候我这里用到了两个参数而且还引入了一个中间件

没有中间件的Redux的过程是:action -> reducer,
而有了中间件的过程就是action -> middleware -> reducer,
使用中间件我们可以对action也就是对dispatch方法进行装饰,
我们可以用它来实现异步action、打印日志、错误报告等功能。

import { createStore, applyMiddleware } from 'redux'

import thunk from 'redux-thunk'

import reducers from './reducers'

const store = createStore(reducers, applyMiddleware(thunk))

export default store

        这时候你可以回头去看看上面对redux-thunk源码的解析,  

   你会发现这样包装后的dispatch非常可爱

   当你在action中需要一个异步操作,并需要在回调中改变state的状态的时候,这就是一个绝佳的解决方案。

5、给你的组件注入这个库

在index.html里

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux'
import store from './store'

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>, 
    document.getElementById('root')
);

我们可以参照源码

//这里需要传store所以我们使用Provider的时候把store传入//那么我们引入了Provider它为我们做了什么呢?
export function createProvider(storeKey = 'store') {
    const subscriptionKey = `${storeKey}Subscription`

    class Provider extends Component {    //将外部的store对象放入context对象中,使子孙组件上的connect可以直接访问到context对象中的store。
        getChildContext() {
          return { [storeKey]: this[storeKey], [subscriptionKey]: null }
        }
     //constructor是Provider初始化时,用于获取props的store对象
        constructor(props, context) {
          super(props, context)
          this[storeKey] = props.store;
        }
     //首先,它把它引入的内容全部变成它的子级元素,     //并且由于它处于整个index.html的最外层     //所以被它包裹的每一个元素都可以接收redux的store数据作为props 
        render() {
          return Children.only(this.props.children)                        //this.props.children用于获取当前组件的所有子组件                        //children.only表示用于获取仅有的一个子组件,没有或者超过一个均会报错.                         //所以注意: 确保Provider组件的直接子级为单个封闭元素,切勿多个组件平行放置                        //引申问题:当这个项目需要用到router时该怎么办?把router包在倒数第二层,Provider在最外层
        }
    }

    if (process.env.NODE_ENV !== 'production') {
      Provider.prototype.componentWillReceiveProps = function (nextProps) {
        if (this[storeKey] !== nextProps.store) {
          warnAboutReceivingStore()
        }
      }
    }

    Provider.propTypes = {
        store: storeShape.isRequired,
        children: PropTypes.element.isRequired,
    }
    Provider.childContextTypes = {
        [storeKey]: storeShape.isRequired,
        [subscriptionKey]: subscriptionShape,
    }

    return Provider
}

export default createProvider()

6、page下的home页面有三个文件

actionTypes.js

export const GET_HOME_DATA = 'home/get_home_data'

你起什么名字都可以,只要不重复,你开心就好了。

  这个名字贯穿了一条修改路线,你会发现接下来你的actionCreator.js和你的reducer.js里都用到了这个名字,不同的名字对应不同的数据操作,记号它,为了便于记号它,我为它专门设置了自己的actionType.js

actionCreator.js

import { GET_HOME_DATA } from './actionTypes'

export const loadHomeDataSync = (home) => {
    return {
      type: GET_HOME_DATA,
      home
    }
}
//先异步获取数据,为了避免麻烦我这里用mock数据代替了//再同步返回获取到的数据
export const loadHomeDataAsync = (dispatch) => {
    return () => {
      fetch('/mock/data.json')
        .then(response => response.json())
        .then(result => {
          dispatch(loadHomeDataSync(result.data))
        })
    }
}

reducer.js

//给你要用的数据设置初值,并且当新的数据来了以后,对数据做你想要的处理  
//我这里是当原数据为空,state为新数据,原数据有的话,和新数据进行合并返回一个新的state

import { GET_HOME_DATA } from './actionTypes'

const defaultState = {
    home:null
}

export default (state=defaultState, action) => {
    if (action.type === GET_HOME_DATA) {
      if(!!state.home){
        return {
          home: [...state.home,...action.home]
        }
      }
      else{
        return {
          ...state,
          home: [...action.home]
        }
      }
    }
    return state
}

7、page下的view下的Home.js

import React, { Component } from 'react';

import { loadHomeDataAsync } from '../actionCreator'
//connect作用:连接React组件与 Redux store
import { connect } from 'react-redux'

const mapState = (state) => {
    return {
      home: state.home.home
    }
}
  
const mapDispatch = (dispatch) => {
    return {
      loadCategories () {
        dispatch(loadHomeDataAsync(dispatch))
      }
    }
}

class Home extends Component {
  componentDidMount(){
    this.props.loadCategories()    //在这里调用,当然,你想在哪调用都可以
  }
  render() {
    console.log(this.props.home)
    return (
      <div>home</div>
    );
  }
}

export default connect(mapState,mapDispatch)(Home);//记得在这里把他们connect起来

        那么connect他究竟为我们做了什么呢?

  为什么connect后面跟两个括号?

  它的基础作用是:

    a、从context里获取store
    b、在componentWillMount 里通过mapStateToProps获取stateProp的值
    c、在componentWillMount 里通过mapDispatchToProps获取dispatchProps的值
    d、在componentWillMount 里订阅store的变化
    e、将获得的stateProp,dispatchProps,还有自身的props合成一个props传给下面的组件

源码参考:

export function createConnect({
  connectHOC = connectAdvanced,
  mapStateToPropsFactories = defaultMapStateToPropsFactories,
  mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
  mergePropsFactories = defaultMergePropsFactories,
  selectorFactory = defaultSelectorFactory
} = {}) {
  return function connect(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    {
      pure = true,
      areStatesEqual = strictEqual,
      areOwnPropsEqual = shallowEqual,
      areStatePropsEqual = shallowEqual,
      areMergedPropsEqual = shallowEqual,
      ...extraOptions
    } = {}
  ) {
    const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
    const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
    const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

    return connectHOC(selectorFactory, {
      // used in error messages
      methodName: 'connect',

       // used to compute Connect's displayName from the wrapped component's displayName.
      getDisplayName: name => `Connect(${name})`,

      // if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
      shouldHandleStateChanges: Boolean(mapStateToProps),

      // passed through to selectorFactory
      initMapStateToProps,
      initMapDispatchToProps,
      initMergeProps,
      pure,
      areStatesEqual,
      areOwnPropsEqual,
      areStatePropsEqual,
      areMergedPropsEqual,

      // any extra options args can override defaults of connect or connectAdvanced
      ...extraOptions
    })
  }
}  connect接收四个参数:mapStateToProps,mapDispatchToProps,mergeProps,optipons返回:一个注入了 state 和 action creator 的 React 组件
  mapStateToProps
  传入:state,ownProps  输出:stateProps

   
   mapDispatchToProps
 这个非常关键,如果定义了这个参数,就会监听redux store的变化,没有的话,就不会。 该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。 同时,如果指定了第二个ownProps,这个参数的值为传入到组件的props,只要组件接受到新的props,mapStateToProps也会被调用
   mergeProps(function)

   stateProps,dispatchProps,自身的props将传入到这个函数中。   默认是Object.assign({}, ownProps, stateProps, dispatchProps)
 
  完整:
connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值