Redux,本身就是一个单纯的状态管理者,我们不追溯它的历史,从使用角度来说:它提供一个全局的对象store,store中包含state对象用以包含所有应用数据,并且store提供了一些reducer方法。这些方法可以自定义,使用调用者得以改变state的值。state的值仅为只读,如果需要更改则必须只能通过reducer。

Redux

  • 核心对象:store
  • 数据存储:state
  • 状态更新提交接口:==dispatch==
  • 状态更新提交参数:带type和payload的==Action==
  • 状态更新计算:==reducer==
  • 限制:reducer必须是纯函数,不支持异步
  • 特性:支持中间件

React Redux

在recat中不使用redux 时遇到的问题

在react中组件通信的数据是单向的,顶层组件可以通过props属性向下层组件传递数据,而下层组件不能向上层组件传递数据,要实现下层组件修改数据,需要上层组传递修改数据的方法到下层组件,当项目越来越的时候,组件之间传递数据变得越来越困难

在react中加入redux 的好处

使用redux管理数据,由于Store独立于组件,使得数据管理独立于组件,解决了组件之间传递数据困难的问题

使用redux

下载redux

npm install redux react-redux

redux 工作流程

  1. 组件通过 dispatch 触发action
  2. store 接受 action 并将 action 分发给 reducer
  3. reducer 根据 action 类型对状态进行更改并将更改后的数据返回给store
  4. 组件订阅了store中的状态,store中的状态更新会同步到组件

使用react redux实现计数器

1.创建项目,并安装 redux

# 如果没有安装react脚手架则执行这条命令安装reate脚手架
npm install -g create-react-app
# 创建reate项目
create-react-app 项目名
# 进入项目
cd 项目名
# 安装 redux
npm install redux reate-redux

2.引入redux,并根据开始实现的代码在react中实现计数器

//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore } from 'redux';

const initialState = {
  count: 0
}
function reducer(state = initialState, action) {
  switch (action.type) {
    case 'increment':
      return {
        count: state.count   1
      }
    case 'decrement':
      return {
        count: state.count - 1
      }

    default:
      return state
  }
}
const store = createStore(reducer)

const increment = {
  type: 'increment'
}

const decrement = {
  type: 'decrement'
}

function Count() {
  return <div>
    <button onClick={() => store.dispatch(increment)}> </button>
    <span>{store.getState().count}</span>
    <button onClick={() => store.dispatch(decrement)}>-</button>
  </div>
}

store.subscribe( () => {
  console.log(store.getState())
  ReactDOM.render(
    <React.StrictMode>
      <Count />
    </React.StrictMode>,
    document.getElementById('root')
  );
})

ReactDOM.render(
  <React.StrictMode>
    <Count />
  </React.StrictMode>,
  document.getElementById('root')
);

明显以上方式虽然可以实现计数器的功能,但在实际项目中肯定不能这样使用,因为组件一般都在单独的文件中的,这种方式明显在其他组件中并不能获取到Store。

计数器案例代码优化-让store全局可访问

为了解决Store获取问题需要使用react-redux来解决这个问题,react-redux给我们提供了Provider组件和connect方法

Provide 组件

是一个组件 可以吧创建出来的store 放在一个全局的地方,让组件可以拿到store,通过provider组件,将 store 放在了全局的组件可以够的到的地方 ,provider要求我们放在最外层组件

connect

connect 帮助我们订阅store中的状态,状态发生改变后帮助我们重新渲染组件

通过 connect 方法我们可以拿到 store 中的状态 把 store 中的状态映射到props中

通过 connect 方法可以拿到 dispatch 方法

connect 的参数为一个函数 这个函数可以拿到store中的状态,要求我们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性

connect 调用后返回一个函数 返回的这个函数继续调用需要传入组件告诉connect需要映射到那个组件的props

新建 Component 文件夹、创建 Count.js 文件

import React from 'react'

function Count() {
    return <div>
        <button onClick={() => store.dispatch(increment)}> </button>
        <span>{store.getState().count}</span>
        <button onClick={() => store.dispatch(decrement)}>-</button>
    </div>
}

export default Count

引入 Provider 组件放置在最外层,并制定store

ReactDOM.render(
  // 通过provider组件 将 store 放在了全局的组件可以够的到的地方  provider要求我们放在最外层组件
  <Provider store={store}><Count /></Provider>,
  document.getElementById('root')
);

引入 connect 方法 根据 connect 的使用来包裹组件

const mapStateProps = state => ({
    count: state.count,
    a: '1'
})
// connect 的参数为一个函数 这个函数可以拿到store中的状态,要求我们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性
// connect 调用后返回一个函数 返回的这个函数继续调用需要传入组件告诉connect需要映射到那个组件的props
export default connect(mapStateProps)(Count)

改造 Count 组件把 action 复制到该文件中

const increment = {
    type: 'increment'
}

const decrement = {
    type: 'decrement'
}
function Count({count,dispatch}) {
    return <div>
        <button onClick={() => {dispatch(increment)}}> </button>
        <span>{count}</span>
        <button onClick={() => {dispatch(decrement)}}>-</button>
    </div>
}

现在项目已经可以运行了但是Count组件中的 提交Action的那一长串代码影响视图的可读性,所以代码还是需要优化

计数器案例代码优化-让视图中的代码可读性更高

我们希望视图中直接调用一个函数这样视图代码可读性强,这个需要利用connect的第二个参数,第二个参数是一个函数,这个函数的形参就是dispatch方法,要求这个函数返回一个对象,返回的这个对象中的内容都会映射到组件的props属性上

申明一个变量为connect中的第二个参数,在这个变量中返回执行不同action操作的对象

// connect 的第二个参数 这个参数是个函数 这个函数的形参就是dispatch方法 要求返回一个对象 这个对象中的属性会被映射到组件的props上
const mapDispatchToProps = dispatch => ({
    increment (){
        dispatch({
            type: 'increment'
        })
    },
    decrement (){
        dispatch({
            type: 'decrement'
        })
    }
})

// connect 的参数为一个函数 这个函数可以拿到store中的状态,要求我们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性
// connect 调用后返回一个函数 返回的这个函数继续调用需要传入组件告诉connect需要映射到那个组件的props
export default connect(mapStateProps, mapDispatchToProps)(Count)

在组件中结构props在视图中直接绑定事件

function Count({count,increment,decrement}) {
    return <div>
        <button onClick={increment}> </button>
        <span>{count}</span>
        <button onClick={decrement}>-</button>
    </div>
}

通过这次优化我们发现 调用 dispatch 触发action 的方法的代码都是重复的,所以还需要继续优化

优化调用 dispatch 触发action 的方法的重复代码简化

利用 bindActionCreators 来简化 dispatch 触发 action的操作,bindActionCreators来帮助我们生成执行action动作的函数

bindActionCreators 有两个参数,第一个参数为 执行action的对象,第二个参数为 dispatch方法

分离action操作,新建store/actions/counter.actions.js文件把执行action操作单独放在这个文件并导出

export const increment = () => ({type: 'increment'})
export const decrement = () => ({type: 'decrement'})

在Count.js中导入关于计数器的action,用bindActionCreators方法来生成dispatch执行action函数

import { bindActionCreators } from 'redux'
import * as counterActions from './../store/actions/counter.actions'


const mapDispatchToProps = dispatch => (bindActionCreators(counterActions, dispatch))
// connect 的参数为一个函数 这个函数可以拿到store中的状态,要求我们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性
// connect 调用后返回一个函数 返回的这个函数继续调用需要传入组件告诉connect需要映射到那个组件的props
export default connect(mapStateProps, mapDispatchToProps)(Count)

代码优化到这里我们发现,redux的代码与组件融合在一起,所以我需要拆分成独立的,为什么要抽离redux呢?因为我们要让我们的代码结构更加合理

重构计数器,把redux相关代码抽离

把reducer函数抽离为单独的文件、把创建store抽离到单独的文件中

因为在reducer 和 actions中我们都写了字符串,但是字符串没有提示所以我们把字符串定义成常量防止我们出现单词错误这种低级错误,新建 src/store/const/counter.const.js 文件

export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

新建 src/store/reducers/counter.reducers.js 文件把 reducer 函数抽离到此文件中

import { INCREMENT, DECREMENT} from './../const/counter.const'
const initialState = {
    count: 0
}
// eslint-disable-next-line import/no-anonymous-default-export
export default (state = initialState, action) => {
    switch (action.type) {
        case INCREMENT:
            return {
                count: state.count   1
            }
        case DECREMENT:
            return {
                count: state.count - 1
            }

        default:
            return state
    }
}

更改actions中的字符串为引入变量

import { INCREMENT, DECREMENT} from './../const/counter.const'

export const increment = () => ({type: INCREMENT})
export const decrement = () => ({type: DECREMENT})

创建src/store/index.js文件 ,在这个文件中创建store 并导出

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

export const store = createStore(reducer)

在引入store的文件中改变为冲项目中store文件中引入store

import React from 'react';
import ReactDOM from 'react-dom';
import Count from './components/Count';
import { store } from './store'
import { Provider } from 'react-redux'
/**
 * react-redux 让react 和 redux 完美结合
*    Provider 是一个组件 可以吧创建出来的store 放在一个全局的地方 让组件可以拿到store
*    connect  是一个方法
 */


ReactDOM.render(
  // 通过provider组件 将 store 放在了全局的组件可以够的到的地方  provider要求我们放在最外层组件
  <Provider store={store}><Count /></Provider>,
  document.getElementById('root')
);

为action 传递参数,对计数器案例做扩展

这个计数器案例已经实现了点击按钮加一减一操作了,现在有个新需求我们需要加减一个数值例如加五减五

这就需要对action传递参数了

在视图中按钮绑定函数传入参数

function Count({count,increment,decrement}) {
    return <div>
        <button onClick={() => increment(5)}> </button>
        <span>{count}</span>
        <button onClick={() => decrement(5)}>-</button>
    </div>
}

在dispacth执行action动作时接受参数并传入到action中

export const increment = payload => ({type: INCREMENT, payload})
export const decrement = payload => ({type: DECREMENT, payload})

在reducers中接收参数并作相应处理

export default (state = initialState, action) => {
    switch (action.type) {
        case INCREMENT:
            return {
                count: state.count   action.payload
            }
        case DECREMENT:
            return {
                count: state.count - action.payload
            }

        default:
            return state
    }
}

原文地址:https://kspf.xyz/archives/10/

到此这篇关于在react中使用redux并实现计数器案例的文章就介绍到这了,更多相关react redux实现计数器内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

使用react+redux实现计数器功能及遇到问题的更多相关文章

  1. ios – React native链接到另一个应用程序

    如果是错误的,有人知道如何调用正确的吗?

  2. ios – React Native – 在异步操作后导航

    我正在使用ReactNative和Redux开发移动应用程序,我正面临着软件设计问题.我想调用RESTAPI进行登录,如果该操作成功,则导航到主视图.我正在使用redux和thunk所以我已经实现了异步操作,所以我的主要疑问是:我应该把逻辑导航到主视图?我可以直接从动作访问导航器对象并在那里执行导航吗?.我对组件中的逻辑没有信心.似乎不是一个好习惯.有没有其他方法可以做到这一点?

  3. 在ios中使用带有React Native(0.43.4)的cocoapods的正确方法是什么?

    我已经挖掘了很多帖子试图使用cocoapods为本地ios库设置一个反应原生项目,但我不可避免地在#import中找到了丢失文件的错误.我的AppDelegate.m文件中的语句.什么是使用反应原生的可可豆荚的正确方法?在这篇文章发表时,我目前的RN版本是0.43.4,而我正在使用Xcode8.2.1.这是我的过程,好奇我可能会出错:1)

  4. ios – 使用react-native-router-flux和redux,如何更新视图组件中的状态?

    为了完整起见,整个代码可以在github找到.解决方法您可能忘记将Launch组件连接到商店.你想要做的是类似于你在ExampleContainer中所做的,即然后正确的值将显示在您的日志中

  5. ios – React Native WebView滚动行为无法按预期工作

    如何确保滚动事件的行为与ReactNative应用程序中的浏览器相同?

  6. ios – React Native – BVLinearGradient – 找不到’React/RCTViewManager.h’文件

    谢谢.解决方法几天前我遇到了完全相同的问题.问题是在构建应用程序时React尚未链接.试试这个:转到Product=>Scheme=>管理方案…=>点击你的应用程序Scheme,然后点击Edit=>转到Build选项卡=>取消选中ParallelizeBuild然后点击标志添加目标=>搜索React,选择第一个名为React的目标,然后单击Add然后在目标列表中选择React并将其向上拖动到该列表中的第一个.然后转到Product=>再次清理并构建项目.这应该有所帮助.

  7. ios – React Native – NSNumber无法转换为NSString

    解决方法在你的fontWeight()函数中也许变成:

  8. ios – React native error – react-native-xcode.sh:line 45:react-native:command not found命令/ bin/sh失败,退出代码127

    尝试构建任何(新的或旧的)项目时出现此错误.我的节点是版本4.2.1,react-native是版本0.1.7.我看过其他有相同问题的人,所以我已经更新了本机的最新版本,但是我仍然无法通过xcode构建任何项目.解决方法要解决此问题,请使用以下步骤:>使用节点版本v4.2.1>cd进入[你的应用]/node_modules/react-native/packager>$sh./packager.s

  9. 反应原生 – 如何通过Xcode构建React Native iOS应用程序到设备?

    我试图将AwesomeProject应用程序构建到设备上.构建成功并启动屏幕显示,但后来我看到一个红色的“无法连接到开发服务器”屏幕.它表示“确保节点服务器正在运行–从Reactroot运行”npmstart“.看起来节点服务器已经运行,因为当我做npm启动时,我收到一个EADDRINUSE消息,表示该端口已经在使用.解决方法从设备访问开发服务器您可以使用开发服务器快速迭代设备.要做到这一点,你的

  10. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部