参考文档
一、Redux介绍
Flux
我们把Flux看做一个框架的理念的话,Redux是Flux的一种实现。Redux是SPA单页面应用程序中多个组件之间共享数据的一种方式。
Flux的基本原则是“单向数据流”,Redux在此基础上强调三个基本原则:
- 唯一数据源 :唯一数据源指的是应用的状态数据应该只存储在唯一的一个Store上。
- 保持状态只读 : 保持状态只读,就是说不能去直接修改状态,要修改Store的状态,必须要通过派发一个action对象完成。
- 数据改变只能通过纯函数完成 :这里所说的纯函数就是把Reducer,Reducer描述了state状态如何修改。Redux这个名字的前三个字母Red代表的就是Reducer,其实Redux名字的含义就是Reducer+Flux。
二、Redux的基本使用
React和Redux事实上是两个独立的产品,一个应用可以使用React而不使用Redux,也可以使用Redux而不使用React,但是如果两者结合使用,没有理由不使用一个名为react-redux的库,这个库能大大简化代码的书写。
npm install redux --save
1. main.js
import React from 'react' // 创建组件、虚拟DOM元素,生命周期
import ReactDOM from 'react-dom' // 把创建好的 组件 和 虚拟DOM 放到页面上展示的
import { createStore } from 'redux'
import App from "./app.jsx"
/**
* 1.创建reducer
这是一个 reducer,形式为 (state, action) => state 的纯函数.。描述了 action 如何把 state 转变
成下一个 state。
state 的形式取决于你,可以是基本类型、数组、对象、甚至是 Immutable.js 生成的数据结构。惟一的要点
是当 state 变化时需要返回全新的对象,而不是修改传入的参数。
*/
function counter(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + 1
case 'MINUS':
return state - 1
default:
return state
}
}
//2.声明 actions
//action 是改变 state 的唯一途径,是一个普通的 javascript 对象,它描述了一个行为且是改变 state 的唯
一途径。从用户UI操作事件、网络请求回调和 WebSocket 等其他地方获得的数据,最终都会通过 dispatch 函数调
用一个 action,从而改变对应的数据。action 必须带有 type 指明具体的行为名称,且能附带上额外的信息。
function addFn() {
return { type:'ADD' }
}
function minusFn() {
return { type:'MINUS' }
}
// 3.创建 Redux store 来存放应用的状态。store对象的API 有 { subscribe, dispatch, getState }。
let store = createStore(counter)
// 4.订阅更新,监听state的变化
store.subscribe(() => console.log(store.getState()))
// 5.触发action
store.dispatch(addFn()) // 1
store.dispatch(addFn()) // 2
store.dispatch(minusFn()) // 1
// 6.渲染页面
function render() {
ReactDOM.render(<App store={store} addFn={addFn} minusFn={minusFn}/>, document.getElementById('app'));
}
render()
// 每当state状态发生变化的时候,重新渲染页面
store.subscribe(render)
2. app.jsx
import React, { Component } from 'react';
class App extends Component {
constructor(props){
super(props)
}
render() {
const store=this.props.store
const addFn=this.props.addFn
const minusFn=this.props.minusFn
const init=store.getState()
return (
<div className="App">
<h1>你好吗?</h1>
{<p>现在的总数是:{store.getState()}</p>}
{<button onClick={ ()=>store.dispatch( addFn() ) }>加1</button>}
{<button onClick={ ()=>store.dispatch( minusFn() ) }>减1</button>}
</div>
);
}
}
export default App;
三、redux异步处理
redux默认情况下只处理同步,想要处理异步,需要上面安装的redux-thunk插件
npm install redux-thunk -S
#1.修改main.js 代码
import { createStore ,applyMiddleware } from 'redux';
import thunk from 'redux-thunk'
//使用applyMiddleware在创建store的时候开启中间件
const store=createStore(couter ,applyMiddleware(thunk))
function addFn() {
return { type:'ADD' }
}
function minusFn() {
return { type:'MINUS' }
}
function addAsynFn() {
return dispatch=>{
setTimeout(()=>{
dispatch(addFn())
},2000)
}
}
function render() {
ReactDOM.render(<App store={store} addFn={addFn} minusFn={minusFn} addAsyncFn={addAsynFn}/>, document.getElementById('app'));
}
#2.修改app.jsx代码
render() {
const store=this.props.store
const addFn=this.props.addFn
const minusFn=this.props.minusFn
const addAsynFn=this.props.addAsyncFn
console.log(addAsynFn)
const init=store.getState()
return (
<div className="App">
<h1>你好吗?</h1>
{<p>现在的总数是:{store.getState()}</p>}
{<button onClick={ ()=>store.dispatch( addFn() ) }>加1</button>}
{<button onClick={ ()=>store.dispatch( minusFn() ) }>减1</button>}
{<button onClick={ ()=>store.dispatch( addAsynFn() ) }>异步加1</button>}
</div>
);
}
四、抽离main.js中的reducer和actions到单独模块
#1.reducers/index.js
//1.创建reducer,描述了如何根据action将state修改为下一个state
export function counter(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + 1
case 'MINUS':
return state - 1
default:
return state
}
}
#2.actions/action.js
//2.声明 actions
export function addFn() {
return { type:'ADD' }
}
export function minusFn() {
return { type:'MINUS' }
}
export function addAsynFn() {
return dispatch=>{
setTimeout(()=>{
dispatch(addFn())
},2000)
}
}
#3.main.js
import React from 'react' // 创建组件、虚拟DOM元素,生命周期
import ReactDOM from 'react-dom' // 把创建好的 组件 和 虚拟DOM 放到页面上展示的
import { createStore,applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { counter } from './reducers/index'
import {addFn,minusFn,addAsynFn} from './actions/action'
import App from "./app.jsx"
// 3.创建 Redux store 来存放应用的状态。store对象的API 有 { subscribe, dispatch, getState }。
let store = createStore(counter,applyMiddleware(thunk))
// 4.订阅更新,监听state的变化
store.subscribe(() => console.log(store.getState()))
// 5.触发action
store.dispatch(addFn()) // 1
store.dispatch(addFn()) // 2
store.dispatch(minusFn()) // 1
// 6.渲染页面
function render() {
ReactDOM.render(<App store={store} addFn={addFn} minusFn={minusFn} addAsyncFn={addAsynFn}/>, document.getElementById('app'));
}
render()
// 每当state状态发生变化的时候,重新渲染页面
store.subscribe(render)
#4. app.jsx 没有变化
五、react和redux的结合使用
- 插件:react-redux
- 不适用subscribe发布事件
- 提供provider和connect两个接口
- provider作用:如果我们手动将state对象一层一层的传入容器组件应用。小还好说,大应用深层的组件简直累死了,绝对让你传到怀疑人生。react-redux提供了Provider组件让我们省了不少功夫,用法就是在我们根组件外部嵌套一层Provider,传入store (使用全局的store有风险)这样所以的子组件都可以开心地拿到state了 。
- Provider接受store作为其props,并声明为context的属性之一 。子组件在声明了contextTypes之后可以通过this.context.store访问到store。
- connect用于连接 React 组件与 Redux store。连接操作不会改变原来的组件类。反而返回一个新的已与 Redux store 连接的组件类。
- 不过现在我们仅仅是通过展示组件生成了一个容器组件并且将它们连接了起来,但是容器组件中并没有数据和逻辑,只是一具空壳毫无意义,所以我们还需要向这个connect函数中传入两个参数。mapStateToProps负责将state的数据映射到展示组件的this.props。mapDispatchToProps负责定义发送action的函数映射到展示组件的this.props。
npm install react-redux --save
#1.main.js
import React from 'react' // 创建组件、虚拟DOM元素,生命周期
import ReactDOM from 'react-dom' // 把创建好的 组件 和 虚拟DOM 放到页面上展示的
import { createStore,applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { counter } from './reducers/index'
import { Provider } from 'react-redux'
import App from "./app.jsx"
// 3.创建 Redux store 来存放应用的状态。store对象的API 有 { subscribe, dispatch, getState }。
let store = createStore(counter,applyMiddleware(thunk))
// 4.订阅更新,监听state的变化
store.subscribe(() => console.log(store.getState()))
ReactDOM.render(
<Provider store={store}>
< App />
</Provider>
, document.getElementById('app'));
#2.app.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux'
import {addFn,minusFn,addAsynFn} from './actions/action'
class App extends Component {
constructor(props){
super(props)
}
render() {
const num=this.props.num
const addFn=this.props.addFn
const minusFn=this.props.minusFn
const addAsynFn=this.props.addAsynFn
console.log(addAsynFn)
return (
<div className="App">
<h1>你好吗?</h1>
{<p>现在的总数是:{num}</p>}
{<button onClick={ addFn }>加1</button>}
{<button onClick={ minusFn }>减1</button>}
{<button onClick={ addAsynFn }>异步加1</button>}
</div>
);
}
}
//将state状态映射到属性里面,之后可以通过props获取
const mapStatetoProps=(state)=>{
return {num:state}
}
//addFn 自动有了dispatch的功能 onClick={addFn} ; addFn minusFn minusFn会被映射到props里面
const mapDispatchToProps={addFn,minusFn,addAsynFn}
//为App组件提供数据和逻辑。mapStateToProps负责将state的数据映射到展示组件的this.props。mapDispatchToProps负责定义发送action的函数映射到展示组件的this.props
App=connect(mapStatetoProps,mapDispatchToProps)(App)
export default App;
#3.其他 reducers/index.js 和 actions/action.js 无变化
六、多个reducer之间的合并问题
#1. reducers/counter.js
export default function counter(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + 1
case 'MINUS':
return state - 1
default:
return state
}
}
#2. reducers/city.js
export default function location(state = '无锡', action) {
switch (action.type) {
case 'CHOOSECITY':
return action.city
default:
return state
}
}
#3. reducers/index.js
import {combineReducers} from 'redux'
//redux提供的用于多个reducer合并的方法
// 里面是个对象。罗列需要合并的reducer
import counter from './counter'; //项目中需要的reducer
import city from './city'; //项目中需要的reducer
console.log(combineReducers)
export default combineReducers({counter,city})
#4.actions/action.js
export function addFn() {
return { type:'ADD' }
}
export function minusFn() {
return { type:'MINUS' }
}
/*该action接收参数,在dispatch当前action的时候可以传递参数*/
export function changeCityFn(name) {
return { type:'CHOOSECITY',city:name }
}
export function addAsynFn() {
return dispatch=>{
setTimeout(()=>{
dispatch(addFn())
},2000)
}
}
#5. main.js
import React from 'react' // 创建组件、虚拟DOM元素,生命周期
import ReactDOM from 'react-dom' // 把创建好的 组件 和 虚拟DOM 放到页面上展示的
import { createStore,applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducers/index'
import { Provider } from 'react-redux'
import App from "./app.jsx"
let store = createStore(reducer,applyMiddleware(thunk))
store.subscribe(() => console.log(store.getState()))
ReactDOM.render(
(<Provider store={store}>
< App />
</Provider>)
, document.getElementById('app'));
#4. app.jsx
import React from 'react'
import { connect } from 'react-redux'
import {addFn,minusFn,addAsynFn,changeCityFn} from '../actions/action'
class App extends React.Component {
constructor(props, context) {
super(props,context)
}
render() {
const num=this.props.num
const city = this.props.city
const addFn=this.props.addFn
const minusFn=this.props.minusFn
const addAsynFn=this.props.addAsynFn
const changeCityFn=this.props.changeCityFn
return <div>
<h1>你好吗?</h1>
<h1>{city}</h1>
{<p>现在的总数是:{num}</p>}
{<button onClick={ addFn }>加1</button>}
{<button onClick={ minusFn }>减1</button>}
{<button onClick={ addAsynFn }>异步加1</button>}
{/* 这里通过箭头函数dispatch对应的方法,并传递参数 */}
{<button onClick={ ()=>{
changeCityFn("苏州")
} }>改变城市</button>}
这是home
</div>
}
}
//将state状态映射到属性里面,之后可以通过props获取
const mapStatetoProps=(state)=>{
return {num:state.counter,city:state.city}
}
//addFn 自动有了dispatch的功能 onClick={addFn} ; addFn minusFn minusFn会被映射到props里面
const mapDispatchToProps={addFn,minusFn,addAsynFn,changeCityFn}
//为App组件提供数据和逻辑。mapStateToProps负责将state的数据映射到展示组件的this.props。mapDispatchToProps负责定义发送action的函数映射到展示组件的this.props
App=connect(mapStatetoProps,mapDispatchToProps)(App)
export default App