上一篇中,我们简单讲了
Redux
的使用,但是,如何规范的在React
中去使用Redux
呢,这篇文章里我们会讲到,但是,也是一个痛苦的不适用的方式,它采用手动连接Redux
和React
,繁琐,不易读,但是作为自学的笔记,我还是要把这一步给写下来。
那么什么叫规范呢?
- 组件化还是王道!所以,我们要把
store.dispatch
方法传递给组件,让对应的组件在自己内部去调用修改状态。 -
subscribe
去订阅render
函数,监听并自动刷新渲染。 - 把
Redux
相关内容,移到单独的文件index.redux.js
去单独管理
开始写代码:
-
create-react-app
去创建项目,npm
安装redux
- 清除
./src
目录中所有文件 -
./src
目录中创建一个名为index.redux.js
的文件,把和Redux
相关的内容移至文件中,首先抽出reducer
定义的代码到index.redux.js
文件中,并向外暴露出以供外部使用reducer
来生成store:
// 定义成常量,尽量避免使用魔法值
const HIRE = 'hireEmployee'
const FIRE = 'fireEmployee'
// 导出并且定义reducer
export function manageCompany(total = 1, action) {
switch (action.type) {
case HIRE:
return total + 1;
case FIRE:
return total - 1;
default:
return 1;
}
}
- 原来我们是手动给
store.dispatch()
传参一个action type
对象,现在我们把其改成:调用方法返回对应action type
对象,并且移至index.redux.js
文件,并export
出来供外部调用,这些函数也成为action creator
:
// type为HIRE的action
export function hire() {
return {type : HIRE}
}
// type为FIRE的action
export function fire() {
return {type : FIRE}
}
这样,我们就把跟reducer
相关的内容(reducer
&action creator
)都移到了index.redux.js
文件中。
- 接下来就是在外部使用这个封装的
index.redux.js
文件了,此案例中我们直接在index.js
中使用,首先还是创建store
对象,那么我们需要createStore()方法
以及相应的reducer
:
import { createStore } from 'redux'
import { manageCompany } from './index.redux'
// 通过reducer创建store
const store = createStore(manageCompany)
- 然后在
index.js
中,去渲染组件,并且由于组件需要store
对象去获取状态以及派发事件,我们要把创建的store
对象传给组件,为了让subscribe
能够去调用,我们把渲染动作再封装在一个方法中:
// 定义一个render()函数去渲染页面,把store和对应的方法都传给组件App
function render() {
ReactDom.render(
<App store={store} hire={hire()} fire={fire()}/>,
document.getElementById('root')
)
}
-
./src
目录中创建一个名为App.js
的组件,用以演示subscribe
自动渲染的效果,并且组件内部去触发修改,调用传入的store
对象的dispatch()
方法。
import React , { Component } from 'react'
//由于组件需要store对象去获取状态以及派发事件,所以我们用属性的方式传入一个store对象
class App extends Component {
handlerCompanyManagement(action) {
this.props.store.dispatch(action)
console.log(`公司现在有${this.props.store.getState()}人`);
}
render() {
const store = this.props.store
return (
<div>
<h1>公司现在有{store.getState()}人</h1>
<p onClick={this.handlerCompanyManagement.bind(this,this.props.hire)}>雇佣一人</p>
<p onClick={this.handlerCompanyManagement.bind(this,this.props.fire)}>开除一人</p>
</div>
)
}
}
export default App
- 最后,每次点击
雇佣一人
或者开除一人
触发的事件,就会根据传入的方法返回的action type
去调用reducer
产生新的状态,也就对应的实现了状态变更,然后就是自动根据状态的修改去刷新显示了:
//我们把`index.js`中封装的`render`方法中,所以需要先手动调用一次才能看到显示
render()
// 然后再把render方法传入subscribe作为监听变化时自动刷新调用的方法
store.subscribe(render)
react
连接redux
的步骤梳理:
1、封装了reducer
相关的内容的文件中,需要export
出对应的reducer
供外部创建store
,并且,提供action creator
去返回action type
。
2、外部组件中,我们新建store
,还有导入reducer
中export
出的action creator
返回的action type
,都以组件属性的形式传给组件,再用subscribe
订阅变化时执行的函数。
3、组件内部通过属性获取store
,获取到了store
也就相当于获取了状态(store.getState
),派发方法(store.dispatch
)执行action
4、组件内部通过属性还获取了action creator
返回的action type
,触发派发方法(store.dispatch
)根据action type
(以及旧的state)就会生成新的state
,同时外部的subscribe
则会执行订阅的函数(一般是刷新渲染的函数),这样整个流程就完成了。
这样就基本实现了组件、
redux
分离的操作,但是这还是属于手动连接的范畴,依旧不是很方便。后面会讲到自动连接,会没那么痛苦。
上代码:
./src/index.js
import React from 'react'
import ReactDom from 'react-dom'
import App from './App'
import { createStore } from 'redux'
import { manageCompany, hire ,fire} from './index.redux'
// 通过reducer创建store
const store = createStore(manageCompany)
// 定义一个render()函数去渲染页面
function render() {
ReactDom.render(
<App store={store} hire={hire()} fire={fire()}/>,
document.getElementById('root')
)
}
// 先手动调用一次看看页面效果
render()
// 然后再把render方法传入subscribe作为监听变化时自动刷新调用的方法
store.subscribe(render)
./src/App.js
import React , { Component } from 'react'
class App extends Component {
handlerCompanyManagement(action) {
this.props.store.dispatch(action)
console.log(`公司现在有${this.props.store.getState()}人`);
}
render() {
const store = this.props.store
return (
<div>
<h1>公司现在有{store.getState()}人</h1>
<p onClick={this.handlerCompanyManagement.bind(this,this.props.hire)}>雇佣一人</p>
<p onClick={this.handlerCompanyManagement.bind(this,this.props.fire)}>开除一人</p>
</div>
)
}
}
export default App
./src/index.redux.js
// 定义成常量,尽量避免使用魔法值
const HIRE = 'hireEmployee'
const FIRE = 'fireEmployee'
// 导出并且定义reducer
export function manageCompany(total = 1, action) {
switch (action.type) {
case HIRE:
return total + 1;
case FIRE:
return total - 1;
default:
return 1;
}
}
// type为HIRE的action
export function hire() {
return {type : HIRE}
}
// type为FIRE的action
export function fire() {
return {type : FIRE}
}