1、redux的使用:
(1)基本概念和 API:
1.1 Store
- Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。
- Redux 提供
createStore
这个函数,用来生成 Store。createStore
函数接受另一个函数作为参数,返回新生成的 Store 对象。
import { createStore } from 'redux';
const store = createStore(reducer);
1.2 State
state就是store里面存储的数据,store里面可以拥有多个state,Redux规定一个state对应一个View,只要state相同,view就是一样的,反过来也是一样的,可以通过store.getState( )获取。
import {createStore} from 'redux'
const store=createStore(fn);
const state=store.getState()
1.3 Action
state的改变会导致View的变化,但是在redux中不能直接操作state也就是说不能使用this.setState来操作,用户只能接触到View。在Redux中提供了一个对象来告诉Store需要改变state。Action是一个对象其中type属性是必须的,表示Action的名称,其他的可以根据需求自由设置。
const action={
type:'ADD_TODO',
payload:'redux原理'
}
在上面代码中,Action的名称是ADD_TODO,携带的数据是字符串‘redux原理’,Action描述当前发生的事情,这是改变state的唯一的方式
1.4 store.dispatch( )
store.dispatch( )是view发出Action的唯一办法。
store.dispatch({
type:'ADD_TODO',
payload:'redux原理'
})
store.dispatch接收一个Action作为参数,将它发送给store通知store来改变state。
1.5 Reducer
- Store收到Action以后,必须给出一个新的state,这样view才会发生变化。这种state的计算过程就叫做Reducer。
Reducer是一个纯函数,他接收Action和当前state作为参数,返回一个新的state。- 注意:Reducer必须是一个纯函数,也就是说函数返回的结果必须由参数state和action决定,而且不产生任何副作用也不能修改state和action对象
const reducer =(state,action)=>{
switch(action.type){
case ADD_TODO:
return newstate;
default return state
}
}
(2)redux 案例
src/index.js代码:
/**
* src/index.js
*/
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {createStore} from 'redux'
import reducer from './reducers/counter'
// 创建仓库,参数是reducer
const store = createStore(reducer)
const renderDom = () => {
ReactDOM.render(
<React.StrictMode>
{/* 父组件传值给子组件,value就是获取状态中的值,onIncrement和onDecrement是一个执行dispatch的函数 */}
<App
value = {store.getState()}
onIncrement={() => {store.dispatch({type: "INCREMENT"})}}
onDecrement={() => {store.dispatch({type: "DECREMENT"})}}
/>
</React.StrictMode>,
document.getElementById('root')
);
}
renderDom();
// 监听数据变化,只要state变化,就会重新执行renderDom函数
store.subscribe(renderDom)
src/reducers/counter.js 代码
/**
* src/reducers/counter.js
*/
import * as constants from './../constants'
const counter = (state=0, action) => {
switch(action.type) {
case constants.INCREMENT:
return state+1;
case constants.DECREMENT:
return state-1
default:
return state;
}
}
export default counter
src/App.js 代码
/**
* src/App.js
*/
import React from 'react';
export default class App extends React.Component{
render() {
console.log (this.props)
let {value, onIncrement, onDecrement} = this.props
return (
<div className="App">
<p>{value}</p>
<button onClick={onIncrement}>加1</button>
<button onClick={onDecrement}>减1</button>
</div>
)
}
}
2、react-redux
- 由于上面使用redux时,每次修改state都要重新调用render函数。于是引入了react-redux。
src/index.js 代码
/**
* src/index.js
*/
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {createStore} from 'redux'
import reducer from './reducers/counter'
// 引入react-redux中的Provider,是一个组件,只要包含在这个组件内的页面都可以使用store
import { Provider } from "react-redux"
// 创建仓库,参数是reducer
const store = createStore(reducer)
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
src/App.js 代码
/**
* src/App.js
*/
import React from 'react';
import {connect} from 'react-redux'
import {increment, decrement} from './actions/counter'
class App extends React.Component{
render() {
console.log (this.props)
let {counter, increment, decrement} = this.props
return (
<div className="App">
<p>{counter}</p>
<button onClick={()=>increment(5)}>加</button>
<button onClick={()=>decrement(10)}>减</button>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
counter:state
}
}
const mapDispatchToProps = (dispatch) => {
return {
increment: (num) => {dispatch(increment(num))},
decrement: (num) => {dispatch(decrement(num))}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
其中,新增了actions/counter.js文件
import * as constants from './../constants'
export function increment (num) {
console.log(num)
return {
type: constants.INCREMENT,
num
}
}
export function decrement (num) {
return {
type: constants.DECREMENT,
num
}
}
如果 actions/counter.js 中很多方法,调用起来就很复杂,可以整合一下。
src/App.js 代码
/**
* src/App.js
*/
import React from 'react';
import {connect} from 'react-redux'
// import {increment, decrement} from './actions/counter'
import * as counterActions from './actions/counter'
import {bindActionCreators} from 'redux'
class App extends React.Component{
render() {
console.log (this.props)
let {counter, counterActions} = this.props
return (
<div className="App">
<p>{counter}</p>
<button onClick={()=>counterActions.increment(5)}>加</button>
<button onClick={()=>counterActions.decrement(10)}>减</button>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
counter:state
}
}
const mapDispatchToProps = (dispatch) => {
return {
counterActions: bindActionCreators(counterActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
现在是只有一个counter模块,如果又有user模块的数据,那就要使用到combineReducers了
reducers文件夹里新建user.js文件,创建一个user的reducers
import { FETCH_USER_SUCCESS } from "../constants"
const initialState = {
user:{}
}
const user = ( state = initialState,action ) => {
switch(action.type){
case FETCH_USER_SUCCESS:
return {
user: action.user
};
default:
return state;
}
}
export default user
reducers文件夹里新建index.js文件,将两个reducers整个起来。
import { combineReducers } from "redux"
import counter from "./counter"
import user from "./user"
const rootReducer = combineReducers({
counter,
user
})
export default rootReducer
index.js引入
import rootReducer from "./reducers"
const store = createStore(rootReducer)
在actions文件夹里新建user.js
import { FETCH_USER_SUCCESS } from "../constants"
export const get_user = (user) => {
return {
type: FETCH_USER_SUCCESS,
user
}
};
新建user页面
import React from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import * as userActions from './../actions/user'
class User extends React.Component{
render () {
let {user, userActions} = this.props
console.log(user, userActions)
return(
<div>
{
Object.keys(user.user).length === 0 ? <p>User</p> : <p>{user.user}</p>
}
<button onClick={ () => { userActions.get_user('gfz') } }>getUser</button>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
user: state.user
}
}
const mapDispatchToProps = (dispatch) => {
return {
userActions: bindActionCreators(userActions,dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(User)