在这篇笔记之前,我写过一篇react环境搭建示例(点击这里查看)的笔记,而在写这篇笔记前也在纠结是再次从第一步开始写还是在原来基础上增加修改,后来觉得后面写的可能是一个整个项目,放一篇文章里面可能太庞大复杂了,就在原来的基础上修改吧。如果你需要之前的源码,你可以点击这里下载源码或在评论区留邮箱。
在react-redux中,action,reducer,store这三个是核心。之前创建的什么home,demo文件都是纯视图文件,可以放到component文件夹中。调整后的文件目录是下面这个结构:
这种所有的actions统一放到actions文件夹中,所有的reducer统一放到reducer文件夹中的形式在小的应用或者说练习中可以比较清晰,但在大的应用中可能会让文件引用非常混乱,如果修改某个文件,比较难找到关联的文件,好点的做法是按照功能模块划分文件夹。为了比较清晰的看出各个模块之间的关联,就先弄成这种造型吧。
安装react-redux 和redux模块
npm install redux react-redux --save
修改main.js代码如下:
import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Route, Redirect, Link, Switch } from "react-router-dom";
import Home from "./components/home/home.jsx";
import Demo from "./components/demo/demo.jsx";
import Hello from "./components/hello/hello.jsx";
import { Provider } from 'react-redux';
import configureStore from './store/configureStore.js'
const store = configureStore()
export class App extends React.Component {
render() {
return (
<div>{this.props.children}</div>
)
}
}
const routes = (
<Router>
<div>
<Switch>
<Route path='/' render={(props) => (
<App>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/demo" component={Demo} />
<Route path="/hello" component={Hello} />
</Switch>
</App>
)} />
<Route render={() => (<Redirect to="/" />)} />
</Switch>
</div>
</Router>
)
render(<Provider store={store}>{routes}</Provider>, document.getElementById("root"))
react-redux中数据返回到页面比较重要的两个部分是Provider和connect;provider就是把我们用redux创建的store传递到内部的其他组件,让内部组件享有这个store并对state进行更新。这里我把Provider放在路由的最外层,让里面的所有component都可以拿到store。
新建store文件夹,在文件夹下面新建一个configureStore.js文件,代码如下:
import { createStore } from 'redux'
import rootReducer from '../reducers/index.jsx'
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState
)
return store
}
这里导出了一个configureStore方法,在main.js中将该方法返回的store赋值给Provider的store的属性值。方法里面的store是通过redux提供的createStore方法创建的,这是它的用法;
createStore(reducer, [initialState], enhancer)
第一个参数是传入的reducer,也就是传入到store中的值,第二个是初始的state,我在main.js中没传过来,第三个参数是传入中间件增强createStore的功能的,暂时不管。
在reducers文件夹下面新建一个index.jsx文件,一个hello.reducer.js文件,index.jsx文件代码如下:
import { combineReducers } from 'redux'
import helloReducer from './hello.reducer.js'
const rootReducer = combineReducers({
helloReducer
})
export default rootReducer
index.jsx主要是管理reducers文件夹中的所有reducer,统一向外暴露一个总得rootReducer到configureStore文件中,这里用到了一个combineReducers函数,作用与Object.assign()差不多。
hello.reducer.js文件中的代码如下:
const initialState = {}
export default function helloReducer(state = initialState, action) {
switch (action.type) {
case "CHANGE_WORD":
return action.data
default:
return state
}
}
reducer里面应该是一个纯函数,这里将action传过来的action通过switch再返回去,而在rootReducer引入,然后又暴露给configerStore再通过Provider的store属性传给里面的页面。
在actions文件夹下面新建一个hello.action.js文件,代码如下:
const CHANGE_WORD = "CHANGE_WORD"
export function changeWord(data) {
return {
type: "CHANGE_WORD",
data
}
}
在action文件中可以做很多业务操作,这里直接将页面传过来的data返回给reducer了。这里的type是redux规定的必传的一个参数,为了让action能和对应的reducer处理对应起来。type也应该是唯一的。
在components文件夹下面新建一个hello文件夹,在hello文件夹下面新建一个hello.jsx文件,代码如下:
import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as helloActions from '../../actions/hello.action.js'
class Hello extends React.Component {
render() {
return (
<div>
<p>hello</p>
<hr/>
<p>{this.props.helloReducer && this.props.helloReducer.word}</p>
<hr/>
<button onClick={this.changeWord.bind(this)}>修改</button>
</div>
)
}
changeWord() {
const actions = this.props.helloActions;
actions.changeWord({
word: 'action change after'
})
}
componentDidMount() {
this.props.helloActions.changeWord({
word: 'action change before'
})
}
}
function mapStateToProps(state) {
return {
helloReducer: state.helloReducer
}
}
function mapDispatchToProps(dispatch) {
return {
helloActions: bindActionCreators(helloActions, dispatch)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Hello)
上面的connect是把展示的组件Hello放到容器组件中,这样,在store里面的值发生变化时,页面组件的props能同步的获得更新后的值。并渲染改变的部分。