您的位置:首页 > Web前端 > React

[react-native]redux由浅入深详细教程(一):创建项目结构以及一个最简单的demo

2019-01-06 19:46 232 查看

redux本身是一个有一定难度的东西,对新手来说学起来颇为吃力。所以在这篇文章中,我将从建立一个最简单的例子开始,最开始只使用react-native和redux,然后在后续的教程中,逐渐加上redux-saga,flow,Immutable,code-push等工具,逐步将这个例子拓展可供实际生产环境使用的demo。同时在拓展的过程中逐步讲解redux原理,希望本文能对你有所帮助。

知识要点--必须牢牢掌握的知识,不掌握就没法好好使用redux

拓展阅读--有助于理解redux原理,但是并不会对使用造成太大影响。建议掌握

目录

0.创建项目结构(可选)

0-1.创建目录

0-2.目录描述:

1.编写基于redux的hello world

1-1.安装redux

1-1.创建store

(知识要点)什么是store?

(拓展阅读)redux三大设计原则之一:单一数据源

1-2.创建reducer

1-3.创建页面

1-4.发出action

(知识要点)什么是action?

(拓展阅读)redux三大设计原则之二:State 是只读的

1-5.编写reducer

(知识要点)什么是reducer?

(知识要点)redux三大设计原则之三:使用纯函数来执行修改

1-6.监听store变化

2.知识点总结

2-1.redux执行流程图

2-2.redux三大件:store、action、reducer简述

2-3.redux三大原则:

3.拆分代码(未完待续)

0.创建项目结构(可选)

一个好的项目结构对于项目的维护很有帮助。但是我看了很多react-native的开源项目,也没能找到一个被大多数人一致公认的写法,所以就自作主张,以我平时最习惯的方法进行创建了。对于新手我建议先就这样模仿,等你对redux有一定程度的掌握后,再按照自己的喜好进行改造也不迟。

 

0-1.创建目录

1.在项目根目录创建app文件夹

2.在app文件夹下创建Root.js文件,以及下面6个文件夹

components, constants, pages,redux, reduxConfig, resource, utils

3.修改项目根目录下的index.js文件,复制以下代码(这一步的作用是将APP的入口修改为root.js文件)。

[code]import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';
import Root from './app/root'

AppRegistry.registerComponent(appName, () => Root);

 

0-2.项目结构描述:

components:存放一些通用的自定义组件,如头部组件,按钮等

constants:存放常量。如字符串常量,颜色,url地址等

pages:存放页面相关的组件

redux:存放与redux相关的业务

reduxConfig:存放与redux相关的配置文件

resource:存放静态资源,如图片等。

utils:工具包。

root.js:程序的入口。

 我们的整个项目的结构就算搭建完成了。顺便说一句,我使用的react-native版本是0.55.4。

1.编写基于redux的hello world

要熟练使用redux需要很多理论知识,然而这样对新手来说很不友好。所以我决定用一边写一边说明的方式进行教学。

 

1-1.安装redux

进入项目根目录,打开控制台,利用npm或者yarn下载redux相关的包。

npm install --save redux

如果你使用yarn:

yarn add redux

 

1-1.创建store

打开redux目录下的reduxConfig目录,创建store.js(文件名可自取)文件,并输入以下代码:

[code]import { createStore } from 'redux';
import helloWorld from '../redux/helloWorld/reducers';

const initValue={
helloWorld:'initText'
};

const store=createStore(helloWorld,initValue);

export default store;

在这段代码里,我们利用redux提供的createStore 方法,创建了一个store对象。

(拓展阅读)createStore方法接收三个参数,第一个传入reducer(我们很快会讲到);第二个参数是store的初始值,这样app在启动时,不用我们做一些额外操作,store就能拥有一些数据;第三个参数可选,代表Store Enhancer,我们现在暂时用不到,后面的文章会慢慢介绍。

(知识要点)什么是store?

众所周知,redux是一个状态管理容器(在react-native中,你可以将状态理解为组件中的state)。既然是容器,那么必然要有一个地方用来存放状态,store便起到这样的作用。

你可以把store想象成一个盒子,平时我们把页面中的状态都存放到这个盒子里,需要的时候就从里面取出来。不过有一点需要注意,我们只能从store中读取数据,而不能对其内部的数据进行修改

那么我们要怎么修改状态呢?这就是reducer的任务了。

(拓展阅读)redux三大设计原则之一:单一数据源

引用官方的话:整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

尽管页面中的可能有几十几百个state,但redux都将它们统统保存在一个objcet里面。redux并不禁止创建多个store,但这样做没必要。从单一的数据源中获取state,明显会为开发带来很多便利。

 

1-2.创建reducer

接下来,在redux目录下创建一个名为helloWorld的目录,再在其中创建一个名为reducers.js的文件,并复制以下代码。

[code]const reducers = function(state, action) {
return state;
};

export default reducers;

这个reducer并不具备任何功能,只是创建store的时候必须要有一个reducer函数,所以我们暂时先不管他。

做好这两步,虽然还不具备任何功能,但app已经能够成功执行了。下面就让我们开始正式构建redux版的hello world吧!

 

1-3.创建页面

首先让我们创建这样一个简单的页面:页面中间有一个按钮,点击该按钮页面会alert出一行文字"hello world"。

在root.js文件中复制以下代码:

[code]import React,{Component} from 'react';
import {
View,
Button,
} from "react-native";

import store from './redux/store/configure-store'

class HelloWorld extends Component {

constructor(props){
super(props);
this.state={
alertTexts:store.getState().helloWorld
}
}

render(){
return (
<View style={{
flex:1,
backgroundColor:'#c1c1c1',
alignItems:'center',
justifyContent:'center',
}}>
<Button
onPress={this.sayHelloWorld}
title="press me (hello world!)"
color="#841584"
/>
</View>
);
}

sayHelloWorld=()=>{
alert(this.state.alertTexts)
}
}

export default HelloWorld;

运行不出错的话,你应该在手机或模拟器上看到如下样式。

点击中间那个按钮

在这个页面里,关于redux代码只有两行。

一、是在头部,我们通过import导入了我们创建的store;

二、是在构造器constructor中,我们通过store.getState().helloWorld,获得了store初始化时的值,并赋给了this.state.alertTexts——聪明的你应该已经猜到了,通过store.getState()方法就可以取出state

接下来,就让我们通过redux,将initText修改为hello world吧!

 

1-4.发出action

在redux中,要想修改store中的数据,有且只有一个方法,就是发出一个action。

修改sayHelloWorld方法如下(顺便一提,这种写法是ES6语法中的箭头函数,效果等同于一个function):

[code]    sayHelloWorld=()=>{
store.dispatch({
type:'changeTextToHelloWorld',
word:'hello word'
})
}

这段代码中,我们通过store自带的dispatch方法发出action。

dispatch方法接收一个对象,该对象有两个属性。其中,type属性描述了我们发出的是什么样的action,word则是我们通过该action传递的参数。这样就发出了一个action。

当redux检测到一个action被dispatch时,下一步就会进入reducer里面。让我们接着编写reducer。

(知识要点)什么是action?

action本身不具有任何业务相关的逻辑,你可以理解为action就是让我们告诉redux我们准备做什么事。

就跟调用函数时,我们要用函数名区分不同的函数一样。redux也要用不同的action名称,区分你发出的是哪个action。只不过函数名直接用字面量描述,而action一般用一个对象描述(这个对象至少应该包含一个type属性,type的值便是用来描述action的字符串)。另外,要成功发出一个action只能使用dispatch方法。

(拓展阅读)redux三大设计原则之二:State 是只读的

唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

store设计成只读的,可以防止我们从页面直接对store进行修改,避免了项目较大时的逻辑以及数据交互的混乱。在本文的末尾会有更详细的原因说明。

 

1-5.编写reducer

打开我们之前在reducers目录下创造的index.js文件,修改代码:

[code]const reducers = function(state, action) {
switch (action.type) {
case 'changeTextToHelloWorld':
return {...state, helloWorld: 'helloWorld'};
default:
return state
}
};

export default reducers;

你可以看到reducer里面有两个参数,第一个是state——也就是store本身(准确的说,是保存在store中的state),第二个参数便是我们上面提到的action。当我们dispatch一个action时,redux便会自动地将store和action作为参数,传入reducers函数里。

在reducers函数内部,我们从action中取出type属性(也就是我们用来描述action的字符串),放到switch中进行判断,这样就达到了区分不同action的目的。

当reducers函数return了一个新的对象时,redux紧接着会把store替换成这个新的对象。也就是说,想要修改store这个盒子中的数据,并不是直接修改这个盒子内部的属性,而是直接拿一个新的盒子换掉旧的盒子,这样就间接达到了修改store内部数据的目的。

有些人可能会困扰 {...state, helloWorld: 'helloWorld'} 是什么写法。state前面的三个点,名为扩展运算符,可以将state中的属性展开(点击这里了解详情)。这个写法与Object.assign()作用相同,等值于下面的语句:

[code]Object.assign({},state,helloWorld: 'helloWorld')

(知识要点)什么是reducer?

前面说到,store是一个负责存取状态的Objcet,然而在redux的设计哲学中,你不能直接对store对象本身进行操作redux三大设计原则之二:State 是只读的)。只有当reducer函数return一个新的对象时,旧的store被该对象替换掉,store的数据才会更新。

注意reducers函数的第一个参数(state),其实这个state就是store。还记得在配置store的时候,我们在createStore函数里传入了reducers吗?当reducers被传进去时,redux将其作为回调函数使用;利用回调函数的性质,redux就能自动地将store传进reducers函数里。

总而言之:reducer参数里的state是旧的store,return的state是新的store。当新的store被return时,store中的数据就更新了。

(知识要点)redux三大设计原则之三:使用纯函数来执行修改

这一条也可以描述为:reducer是一个纯函数。

什么是纯函数?纯函数的意思就是,函数的返回结果只依赖于它的参数,而不依赖外部数据;同时在函数执行过程中,也不会对其他外部数据产生影响。纯函数的使用是非常安全的,因为我们不必担心它做出一些出乎我们意料之外的操作。一个纯函数在相同的输入下总会有相同的输出。

正因为直接修改reducer的参数是不被允许的(因为在函数执行的时候,参数实际是外部传进来的对象),我们最后return的是一个新的state,而不是直接修改旧的state,再将旧的state返回。

以下常见操作是不被允许的:使用外部变量,调用另一个函数,从storage中取值,发起网络请求等。

 

1-6.监听store变化

store的数据已经产生变化了,然而页面本身是没法知道store是否变化的,这时候就要用到store的监听功能,页面就能实时获取到store的状态了。

打开root.js文件,在组件HelloWorld的两个生命周期函数里面,分别写入注册和移除store监听的代码:

[code]
class HelloWorld extends Component {
//...省略其他代码

//注册监听
componentDidMount() {
this.storeSubscribe=store.subscribe(()=>{
alert(store.getState().helloWorld)
})
}
//页面注销时移除监听
componentWillUnmount() {
this.storeSubscribe.unsubscribe();
}

//...省略其他代码
}

store.subscribe()即可注册监听。里面接收一个函数作为回调,当监听到store改变的时候就会执行这个回调函数。在回调函数内部,可以通过store.getState()获取到改变后的状态。

这时候我们Reload一次app,再点击中间的按钮,就会惊喜地发现alert出来的语句变成了:

恭喜你,你已经掌握了redux的基本概念。

2.知识点总结

2-1.redux执行流程图

总而言之,redux遵循这样一个执行流程:

1.组件利用store自带的dispatch方法,发出一个action。

2.redux接收到action,将业务分发给对应的reducer处理。

3.reducer处理完毕后,返回处理后的state对象。

4.redux接收到新的state对象后,通知store.subscribe()函数执行。

 

永远牢记redux的执行流程:组件=>action=>reducer=>store=>组件。

虽然看似东西很多,但在大多数情况下,我们只要关注组件、action、reducer这三个东西就好。

2-2.redux三大件:store、action、reducer简述

store:一个对象,保存着所有的state,store是只读的,Redux 应用有且只有一个单一的 store。

action:主要有两个作用:1.描述我们准备执行的动作,通知reducer进行相应的处理;2.向store传递数据,它是store数据的唯一来源。记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。

reducer:根据action中type的不同,处理不同的事件;返回新的state。

2-3.redux三大原则:

1.单一数据源:所有状态都保存在单一的store中

2.state是只读的:不能直接对store进行修改,只能用新的store替换旧的store

3.使用纯函数来执行修改:reducer是只读的

3.拆分代码(未完待续)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: