react仿小米商城2.0,redux升级版

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

前言

redux简介

Redux对于JavaScript应用而言是一个可预测状态的容器。换言之,它是一个应用数据流框架,而不是传统的像underscore.js或者AngularJs那样的库或者框架。Redux最主要是用作应用状态的管理。简言之,Redux用一个单独的常量状态树(对象)保存这一整个应用的状态,这个对象不能直接被改变。当一些数据变化了,一个新的对象就会被创建(使用actions和reducers)。

redux优点

  • 预测始终有一个准确的数据源,就是store, 对于如何将actions以及应用的其他部分和当前的状态同步可以做到绝不混乱。
  • 维护具备可预测结果的性质和严格的组织结构让代码更容易维护。
  • 组织对代码应该如何组织更加严苛,这使代码更加一致,对团队协作更加容易。
  • 测试编写可测试代码的首要准则就是编写可以仅做一件事并且独立的小函数。Redux的代码几乎全部都是这样的函数:短小、纯粹、分离。
  • 服务端渲染可以带来更好的用户体验并且有助于搜索引擎优化,尤其是对于首次渲染。仅仅是把服务端创建的store传递给客户端就可以。
  • 开发者工具开发者可以实时跟踪在应用中正在发生的一切,从actions到状态的改变。
  • 社区与生态圈存在很多支持Redux的社区,使它能够吸引更多的人来使用。

核心概念

Redux核心概念有三个:

  • actions
  • store
  • reducers

image.png

Actions

简单地,Actions就是事件。Actions传递来自这个应用(用户接口,内部事件比如API调用和表单提交)的数据给store。store只获取来自Actions的信息。内部Actions就是简单的具有一个type属性(通常是常量)的JavaScript对象,这个对象描述了action的类型以及传递给store的负载信息。

{
    type: LOGIN_FORM_SUBMIT,
    payload: {username'alex'password'123456'}
}

Actions通过action生成器创建,它们仅仅是是返回action的函数。

function authUser(form) { 
    return { 
        type: LOGIN_FORM_SUBMIT, 
        payload: form 
    } 
}

Reducers

在函数式JavaScript中reducer基于数组reduce方法,接收一个回调(reducer)让你从多个值中获得单个值,整数和,或者一个一系列值的累积。在Redux中,reducer就是获得这个应用的当前状态和事件然后返回一个新状态的函数。理解reducer是怎样工作的至关重要,因为它们完成大部分工作。这是一个非常简单的reducer,通过获取当前state和一个action作为参数,再返回下一个state:

function handleAuth(state, action{
    return _.assign({}, state, {
        auth: action.payload  
    });
}

对于更多复杂的项目,使用Redux提供的combineReducers()实例是必要的(推荐)。它把在这个应用中所有的reducer结合在一起成为单个索引reducer。每一个reducer负责它自己那部分应用的状态,这个状态参数和其他reducer的不一样。combineReducers()实例使文件结构更容易维护。 如果一个对象(state)只改变一些值,Redux就创建一个新的对象,那些没有改变的值将会指向旧的对象而且新的值将会被创建。这对性能是极好的。为了让它更有效率你可以添加 Immutable.js

const rootReducer = combineReducers({
    handleAuth: handleAuth,
    editProfile: editProfile,
    changePassword: changePassword
});

Store

Store对象保存应用的状态并提供一些帮助方法来存取状态,分发状态以及注册监听。全部state由一个store来表示。任何action通过reducer返回一个新的状态对象。这就使得Redux非常简单以及可预测。

import { createStore } from 'redux';
let store = createStore(rootReducer);
let authInfo = {username'alex'password'123456'};
store.dispatch(authUser(authInfo));

页面展示

image.png

组件介绍

Redux

redux的核心概念其实很简单:将需要修改的state都存入到store里,发起一个action用来描述发生了什么,用reducers描述action如何改变state tree 。创建store的时候需要传入reducer,真正能改变store中数据的是store.dispatch API。

React-Redux

Redux 可以集成到任何的 UI 框架中,其中最常见的是 React。React-Redux是我们的官方包,它可以让 React 组件访问 state 和下发 action 更新 store,从而同 Redux 集成起来。

Redux-Thunk

redux-thunk就是一个标准的 Redux middleware。dispatch一个action之后,到达reducer之前,进行一些额外的操作,就需要用到middleware。你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。 换言之,中间件都是对store.dispatch()的增强

Redux DevTools插件

image.png Redux DevTools 可以轻松跟踪应用程序状态更改的时间、位置、原因和方式。Redux的架构允许您记录更改,使用“时间旅行调试”甚至向服务器发送完整的错误报告。

锚点跳转功能

锚点其实就是可以让页面定位到某个位置上的点。在高度较高的页面中经常见到。比如百度的百科页面,wiki中的page内容。

代码

const renderCategory = (categoryList) => {
        return categoryList.map(item => (
            <div className="left-data" key={item.id}>
                <a onClick={() => scrollToAnchor(item.id)}>
                // id为锚点
                    <span>{item.name}</span>
                </a>
            </div>
        ))
      }
    const scrollToAnchor = (anchorName) => {
        // 找到锚点
        if (anchorName) {
            let anchorElement = document.getElementById(anchorName)
            // 如果对应id的锚点存在,就跳转到锚点
            if (anchorElement) {
                anchorElement.scrollIntoView({
                    block: 'start',
                    behavior: 'smooth'
                })
            }
        }
    }

代码展示

目录

image.png

redux总仓

数据仓库的搭建过程

  • 从 react-redux中解构Provider,声明给应用添加数据管理功能
  • store 专有目录下redux中的 createStore 创建仓库实例
  • createStore方法接受reducer 函数

index.js

import { createStore, compose, applyMiddleware } from 'redux'
import thunk from 'redux-thunk' 
import reducer from './reducer'

const composeEnhancers =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer,
    composeEnhancers(
        applyMiddleware(thunk)
    )
)

export default store;

reducer.js

import { combineReducers } from 'redux'
import { reducer as classifyReducer } from '@/pages/Classify/store'

export default combineReducers({
    classify: classifyReducer
})

redux子仓

actionCreators.js

描述了 action 如何把 state 转变成下一个 state。 state 的形式取决于你,可以是基本类型、数组、对象、甚至是 Immutable.js 生成的数据结构。惟一的要点是当 state 变化时需要返回全新的对象,而不是修改传入的参数。 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。

import * as actionType from './constants'
import { getClassifyListRequest } from '@/api/request'

export const changeClassifyList = (data) => ({
    type: actionType.CHANGE_CLASSIFY_LIST,
    data: data
})
export const getClassifyList = () => {
    return (dispatch) => {
        getClassifyListRequest().then(data => {
            const action = changeClassifyList(data.classifylist)
                dispatch(action)
        })
    }
}

constants.js

export const CHANGE_CLASSIFY_LIST = 'CHANGE_CLASSIFY_LIST'

index.js

import reducer from './reducer'
import * as actionCreators from './actionCreators'

export {
    reducer,
    actionCreators
}

reducer.js

这是一个 reducer,形式为 (state, action) => state 的纯函数。reducer 还可以多次运行

import * as actionTypes from './constants'

const defaultState = {
    classifyList: []
}
export default (state = defaultState, action) => {
    switch(action.type) {
        case actionTypes.CHANGE_CLASSIFY_LIST:
            return {
                ...state,
                classifyList: action.data
            }
            
        default:
            return state;
    }
}

状态改变

mapStateTOProps 获取状态,mapDispatchToProps 触发状态的改变

const mapStateToProps = (state) => {
    return {
        categoryList: state.classify.categoryList,
    }
  }

const mapDispatchToProps = (dispatch) => {
    return {
      getCategoryListDispatch() {
        dispatch(actionCreators.getCategoryList())
      }
    }
}

总结

redux的某些地方运用难度还是很大的,我在redux从远程接口拿取数据时经常出错,后面经过对比排查之后才拿取成功。而且小型项目没必要用redux,如果你在想要不要用redux,那就不要用。redux创造者都说过"只有遇到 React 实在解决不了的问题,你才需要 Redux 。"

源码地址react/MiShop

猜你喜欢

转载自juejin.im/post/7125824958550245384