适用场景
UI层简单,没有什么互动的,不需要redux
使用方式复杂,与服务器大量交互,视图层要从多个来源获取数据的,使用redux
比如说:
- 组件状态要功响
- 一个组件更改全局状态
- 一个组件需要改变另一个组件的状态
设计思想
web应用是个状态机,视图与状态一一对应。
所有的状态,保存在一个对象里。
基本使用
npm i -g create-react-app安装包
create-react-app redux-api创建项目
npm i redux安装redux
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux'
// 1.定义reducer
// state:容器的初始状态
// action:修改state的行为类型
// type - 行为类型; payload(可选的)其他数据
function Reducer(state = 0, action) {
const {type} = action
if (type === 'INCREMENT') {
return state + 1
} else if (type === 'DECREMENT') {
return state - 1
} else {
return state
}
}
// 2.创建store
const store = createStore(Reducer, 110)
// 3.获取状态
console.log(store.getState())
// 4.更新状态
// store.dispatch({type:行 为类型,额外参数})
// 发起dispatch就是在调用reducer,dispatch接收的参数叫action
setTimeout(() => {
store.dispatch({
type: 'INCREMENT'
})
}, 1000);
// 5.检测状态变化,驱动视图更新
store.subscribe(() => {
console.log(store.getState())
render()
})
const Counter = (props) => {
return <div>
<h1>{props.value}</h1>
<button>Increment</button>
<button>Decrement</button>
</div>
}
function render() {
ReactDOM.render(<Counter value={store.getState()}></Counter>, document.getElementById('root'));
}
render()
核心概念
-
store
保存数据的容器,通过createStore来生成。
import { createStore } from 'redux'
const store = createStore(Reducer, 110)
-
state
包含所有的数据。
一个state对应一个view。
const state = store.getState()
-
action
view发出action,通知state进行改变。
action是一个对象,type属性必须有,表示名称。其他属性自由设置。
action是改变state的唯一方法。
const action = {
type: 'INCREMENT',
payload: 'learn redux'
}
-
action creator
生成action的函数
const ADD_TODO = '添加todo'
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
const action = addTodo('learn redux')
-
store.dispatch
view通过此方法发出action。也是唯一的方法。
store.dispatch接受action对象作为参数,然后将其发送出去。
store.dispatch({
type: 'INCREMENT',
payload: 'learn redux'
})
store.dispatch(addTodo('learn redux'))
-
reducer
state的计算过程。接受action和当前state作为参数,返回新的state。
function Reducer(state = 0, action) {
const {type} = action
if (type === 'INCREMENT') {
return state + 1
} else if (type === 'DECREMENT') {
return state - 1
} else {
return state
}
}
一般情况下,store.dispatch方法会触发reducer的自动执行,所以store生成时,需要传入reducer
const store = createStore(Reducer, 110)
reducer是纯函数,同样的输入,必定得到同样的输出。
reducer函数里的state不能改变,必须返回一个全新的对象。最好把state设置为只读。
// state是对象
function reducer(state, action) {
return { ...state, ...newState}
}
// state是数组
function reducer(state, action) {
return [ ...state, newItem]
}
-
store.subscribe()
监听方法,state改变,就会立即执行。
把view更新的函数放入其中,就会实现view的自动渲染。
store.subscribe(() => {
console.log(store.getState())
render()
})
如果store.subscribe方法返回函数,调用这个函数,即可解除监听
let unsubscribe = store.subscribe(() => {
connsole.log(store.getState())
})
unsubscribe() // 解除监听
reducer的拆分
项目中state一般很庞大,所以需要对reducer进行拆分
拆分前的代码:
const Reducer = (state = {}, action = {}) => {
const { type, payload } = action
switch (type) {
case 'ADD_CHAT':
return Object.assign({}, state, {
charLog: state.charLog.concat(payload)
})
case 'CHANGE_STATUS':
return Object.assign({}, state, {
statusMessage: payload
})
case 'CHANGE_USERNAME':
return Object.assign({}, state, {
userName: payload
})
default: return state
}
}
拆分后的文件结构

chatLog.js
export default function chatLog (state = [], action) {
const { type, payload } = action
switch (type) {
case 'ADD_CHAT':
return [...state, payload]
default: return state
}
}
statusMessage.js
export default function statusMessage (state = '', action) {
const { type, payload } = action
switch (type) {
case 'CHANGE_STATUS':
return payload
default: return state
}
}
userName.js
export default function userName (state = '', action) {
const { type, payload } = action
switch (type) {
case 'CHANGE_USERNAME':
return payload
default: return state
}
}
index.js
import chatLog from './chatLog'
import statusMessage from './statusMessage'
import userNameChange from './userName'
import { combineReducers } from 'redux'
// 写法1:不推荐
// export default function (state = {}, action = {}) {
// return {
// chatLog: chatLog(state.chatLog, action),
// statusMessage: statusMessage(state.statusMessage, action),
// userName: userNameChange(state.userName, action)
// }
// }
// 写法2:推荐
export default combineReducers({
chatLog,
statusMessage,
userName: userNameChange
})
当需要使用拆分后的reducer时,只需要在src的index.js下
import rootReducer from './reducer'
const store = createStore(rootReducer)
中间件
位于“中间”,为“两侧”提供服务的函数。
redux-logger
npm i redux-logger安装包
index.js中修改下面几处:
import { applyMiddleware, createStore } from 'redux'
import { createLogger } from 'redux-logger'
const logger = createLogger()
const store = createStore(
reducer,
applyMiddleware(logger)
)
再次使用时,可以看到

redux-thunk
用来搭建异步action构造器
npm i redux-thunk安装包
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { applyMiddleware, createStore } from 'redux'
import { createLogger } from 'redux-logger'
import thunk from 'redux-thunk'
import reducer from './reducer'
const logger = createLogger()
const store = createStore(
reducer,
applyMiddleware(thunk, logger)
)
function increment () {
return {type: 'INCREMENT'}
}
function asyncIncrement () {
return function (dispatch, getState) {
setTimeout(() => {
dispatch(increment())
}, 1000);
}
}
store.subscribe(() => {
render()
})
const Counter = (props) => {
return <div>
<h1>{props.value}</h1>
<button onClick={props.increment}>Increment</button>
<button onClick={props.asyncIncrement}>AsyncIncrement</button>
</div>
}
function render() {
ReactDOM.render(
<Counter
value={store.getState().increment}
increment={() => store.dispatch(increment())}
asyncIncrement={() => store.dispatch(asyncIncrement())}></Counter
>,
document.getElementById('root'));
}
render()
react和redux的连接
基本概念
react-redux中组件分两类:ui组件和容器组件
ui组件:只负责ui呈现,没逻辑
容器组件:管理数据和业务逻辑,有内部状态,使用redux的api
connect:用于从ui组件生成容器组件。小案例
目标:实现ui组件和容器组件的分离,实现点击
+1和点击-1的功能
create-react-app react-redux-demo 创建新项目
npm i redux react-redux 安装包
文件结构

CouterContainer.js容器组件
import {connect} from 'react-redux'
import Counter from '../components/Counter'
const mapStateToProps = state => {
return {
value: state
}
}
const mapDispatchToProps = dispatch => {
return {
handleIncrement () {
dispatch({
type: 'INCREMENT'
})
},
handleDecrement () {
dispatch({
type: 'DECREMENT'
})
}
}
}
const CounterContainer = connect (
mapStateToProps,
mapDispatchToProps
) (Counter)
export default CounterContainer
Counter.jsui组件
import React from 'react'
const Counter = (props) => {
return (
<div>
<h1>Counter Component</h1>
<h1>{props.value}</h1>
<button onClick={props.handleIncrement}>点击+1</button>
<button onClick={props.handleDecrement}>点击-1</button>
</div>
)
}
export default Counter
store > index.js
import {createStore} from 'redux'
function reducer (state = 100, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
const store = createStore(reducer)
export default store
index.js引入Provider和store
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
app.js 引入容器组件
import React from 'react';
import CounterContainer from './containers/CouterContainer'
function App() {
return (
<div className="App">
<CounterContainer />
</div>
);
}
export default App;