React入门(十)之redux

1、redux介绍

状态管理,类似于vue中的vueX。

  1. Redux是为javascript应用程序提供一个可预测(根据一个固定的输入,必然会得到一个固定的结果)的状态容器。可以运行于服务端,客户端,原生应用,从Flux演变而来;

  2. 集中的管理react中多个组件的状态;

  3. redux是专门作状态管理的js库,并不是react的插件库,也可以用在其他js框架中,例如vue,但是基本用在react中

2、redux成员

成员作用类型
createStore(reducer,state)创建store实例(state:仓库存放的数据;reducer:对数据的操作)函数
combineReducers合并多个reducer函数
applyMiddleware安装中间件,改装增强redux函数

store成员

成员作用类型
subscribe(回调函数)订阅state变化函数
dispatch(action)发送action 给 reducer函数
getState()获取一次state的值函数
replaceReducer一般在 Webpack Code-Splitting 按需加载的时候用函数

数据流动

component(views)actionreducerstatecomponent(views)
展示state转发的动作,异步业务同步业务处理逻辑, 修改state,并且返回新的state状态收集
store.dispatch—》-------------》《–subscribe
《–getState

示例代码:

1)、组件从store中获取state(数据)):

//创建一个store,
//创建store需要reducer和state。即就是:创建仓库时,需要说清楚仓库中存储的数据(state),以及对数据的操作(reducer)

./src/plugin/redux.js

import { createStore} from "redux";
import state from "../store/state";
import reducer from "../store/reducer";

//1、 创建仓库,并且说清楚,仓库里存储的数据,以及,对仓库数据的操作
// createStore :创建仓库的
// state:仓库里存储的数据
// reducer:对仓库数据的操作
let store = createStore(reducer,state);

export default store;

//2、创建state
//state就是仓促存储的数据(对象)
./src/store/state.js

export default {
    count:0
}

//3、创建reducer
//对仓库数据进行操作的函数(函数)。
//要求:传入旧的state,返回新的state。

./src/store/reducer.js

// reducer要求是个纯函数(在函数内部不能修改函数的参数(输入),要有返回值),它的功能是:传入旧的state,根据action对state做操作,返回新的state。
// 参数:
// state:原始的state
// action:要做的事情,动作的类型
// 返回值:必须要有有,是新的state(修改后的state)。getState()函数会调用reducer

// 因为是纯函数,所以,在函数内部,不能修改state和action。
let reducer = (state,action)=>{
    if(action.type){
      //对state的操作 
    }
    switch(action.type){
        case 添加:……………………return 新的state;break;       
        case 删除:……………………return 新的state;break;       
    }
    return state;//返回新的state
}

export default reducer;

//组件
./src/App.js
import store from "./plugins/redux";

class App extends React.Component {
  state = {

  }
  render = () => (
    <div>
      <div className="App">
        <p>{store.getState().count}</p>
      </div>
    </div>
  )
}

2)、通过组件修改store中的state(数据)):

注意:修改store中的state(数据)后,还需要把数据响应到组件上,就需要使用 subscribe。

修改reducer
./src/store/reducer.js

let reducer = (state,action)=>{
    let {type,payload} = action;
    switch(type){
        case "INCREMENT":{
            console.log("加一");
            return {
                ...state,
                count:state.count+payload
            }
        }
        default:return state;
    }
}

//修改App组件的代码
./src/App.js
class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {
         count:store.getState().count
    }

    store.subscribe(()=>{
        this.setState({
          count:store.getState().count
        });
    })
  }

    inc(){
      store.dispatch({type:"INCREMENT",payload:10});
    }
  
    render = () => (
      <div>
        <div className="App">
          <p>{this.state.count}</p>
          <input type="button" value="增加" onClick={()=>{this.inc()}} />  
        </div>
      </div>
    )
  }

3、流程总结

安装:
npm i --save redux

import {createStore} from 'redux'

//一、创建reducer
//reducer:对仓库(数据)的操作
//参数:
// state:传入的旧数据(原始数据)
// action:对数据的操作
//返回值:操作后的新的数据

const reducer = (state,action)=>{
  let {type,payload}=action    
  swtich (type){
  	case XXXXX :{
    	  //数据的逻辑处理
      	return {
	          ...state,
	          属性名:新的值
	      }
  	}   
  	default:
	    return state
  }
}

//二、创建state对象
// 仓库里的数据
export default {
    count:0
}

//三、创建store对象(仓库)
//使用createStore(对仓库的操作,仓库的数据)
store = createStore(reducer,state)
export default store;

//四、在组件内部使用仓库(如:获取仓库的数据,修改仓库的数据,添加,删除)

import store from '...'

store.getState() //获取状态,执行一次

store.dispatch({type:xxx,payload:ooo}) //发送action给reducer  type是必传参数

store.subscribe(回调)  //订阅 state  更新state时触发

4、提取并定义 Action Creators

./src/store/actionCreators.js

export const increment = payload =>({
    type:"INCREMENT",
    payload
})

export const decrement = payload =>({
    type:"DECREMENT",
    payload
})

App.js组件
./src/App.js

import { increment } from "./store/actionCreators";

store.dispatch(increment(10));

5、action里处理异步

需要安装中间件 redux-thunk ,redux-thunk可以增强dispatch的功能,让dispatch可以接受一个函数作为参数。

./src/plugins/redux.js

//安装中间件改装 redux
import {createStore,applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
let store = createStore(rootReducer,rootState,applyMiddleware(thunk));

./src/store/actionCreators.js

//处理异步
export let ADD =()=>((dispatch)=>{
    axios({
        url:"http://localhost:3000/inc",
    })
    .then(res=>{
        dispatch({
            type:"INCREMENT",
            payload:res.data.num
        })
    })
})


App组件
./src/App.js

import { increment,decrement,ADD } from "./store/actionCreators";

store.dispatch(ADD());

6、combineReducers提取reducer

把一个大的reducer拆成若干个小的。注意:把大的state也分开,并且放在每个reducer。也就是说,每个reducer里面写上它要操作的数据,这样的话,在一个reducer里包含了,数据(state)和数据的操作(reducer)。

// ./src/plugins/myRedux.js

import {createStore,applyMiddleware,combineReducers} from 'redux'
import thunk from 'redux-thunk'
import count from "../store/reducers/count";
import todos from "../store/reducers/todos";

let rootReducer = combineReducers({
    todos:todos,
    count:count
});
//去掉了第二个参数state(因为state拆分到了每个reducer里了)
export default createStore(rootReducer,applyMiddleware(thunk));


// ./src/store/reducers/count.js

// 是当前reducer要操作的数据
let initCount = 8;

const count = (count=initCount,action)=>{    
    switch(action.type){
        case "INCREMENT":{
            return count+action.payload;
        }
        case "DECREMENT":{
            return count-action.payload;
        }
        default: return count;
    }
}

export default count;


// ./src/store/reducers/todos
let initState=[] //当前reducer所操作的数据,放在里自己的模块里。

const todos = (todos, action) => {
  switch (action.type) {
    case "ADD_TODO": {
      return [
        ...todos,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ]
    }

    case "REMOVE_TODO": {
      const { id } = action;
      todos.map((item,index) => item.id ===id && todos.splice(index, 1));
      return [...todos]
    }

    case "CHECK_TODO": {
      const { id } = action;
      todos.map((item,index) => item.id ===id && (todos[index].completed=!todos[index].completed));
      return [...todos]
    }

    default:
      return todos;
  }
};

export default todos;

//删除掉state文件。

//组件里:
写法基本上不变
store.getState().todos

state数据不写在类内部订阅,可以写在主入口文件 订阅store数据的更新

let render = ()=>{
    ReactDOM.render(
      <App/>,
      document.getElementById('root')
    )
};
render();
store.subscribe(render);

7、react-redux

​ 基于redux思想,专门为react使用redux而生,react-redux是连接redux和react组件的桥梁


redux里的问题:

1、组件中出现了大量的store对象

2、 在redux里,凡是使用state里数据的组件,必须加上 store.subscribe() 函数,否则,数据不是响应式的

8、react-redux的API

(react-redux仅有2个API)

  • 组件:可以让组件拿到state(不需要使用传统的subscribe()来监听state重绘组件)

    import {Provider} from "react-redux";
    import store from './redux/store'
    
    ReactDOM.render((
        <Provider store={store}>
            <App/>
        </Provider>
    ), document.getElementById('root'));
    
  • connect(): 链接 ,(返回值)是个高阶组件,用来链接react组件和redux(组件状态要从redux中获取)

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

功能:把store和react组件联系在一起。只要store发生了变化就会调用mapStateToProps方法。Connect方法就是个高阶组件。

 参数1:mapStateToProps是个函数,

功能: 把仓库里的state合并到props里。给mapStateToProps函数传入所有state**,它返回指定的state数据(需要合并到组件props中的state)。返回的state与组件的 props 合并(联系的体现)。所以,当store发生变化时,mapStateToProps方法就会更新组件里的props,那么组件就更新了(因为props变了)。

参数:

​ state:所有的state

返回值:

​ 经过处理后的state(组件里需要的state)。

示例代码:

const mapStateToProps = (state)=>{
      return {
        count:state.count
      }
}
参数2:mapDispatchToProps函数

功能:

​ 把dispatch和props联系起来。传入dispatch,返回绑定好的action方法。

​ 更改数据必须要触发action,所以,mapDispatchToProps把 action 作为组件的props 进行绑定(联系的体现),要派发的函数名,可以是多个函数。mapDispatchToProps 就是用于建立组件跟store.dispatch的映射关系。

参数:

dispatch: 派发

ownProps:当前组件的props,即使用标签时,传入的props

返回值:

​ 对象:表示所有dispatch的对象

示例代码:

import React from "react";
import './App.css';
import {ADD,REDUCE} from "./store/action";
import {connect} from "react-redux";

class App extends React.Component {

  add(){
    this.props.add();
  }

  reduce(){
    this.props.reduce();
  }

  render = () => {
      return (
          <div className="App">
            <p>{this.props.count}</p>
            <input type="button" value=" 加 " onClick={()=>this.add()} />
            <input type="button" value=" 减 " onClick={()=>this.reduce()} />            
          </div>
        )
    }
}

export default connect((state)=>{
  return {
    count:state.count
  }
},dispatch=> ({
  add: () => dispatch(ADD()),
  reduce: () => dispatch(REDUCE())  
}))(App);

react-redux的思路:

1)、用Provider包裹最顶层的组件,提供一个store属性。这样redux任何组件里都可以使用store了。

2)、使用connect()函数来链接react的组件和redux的store。记住:connect不能单独使用,必须要有Provider

9、最佳实现

安装:npm install --save react-redux

//1、主入口文件 index.js
import {Provider} from 'react-redux'
import store from './plugins/redux'

<Provider store={store}>
  <App/>
</Provider>
  
 //2、容器组件里:App组件
 
import {connect} from "react-redux";
 
class App extends React.Component {
  add(){
    //直接用props来调用dispatch,而不需要store
    this.props.dispatch({
      type:"INCREMENT",
      payload:2
    });
  }
    
  render = () => (
   	 <div className="App">
        <p>{this.props.count}</p>  //  使用props可以直接拿到state里的数据,而不需要store
        <input type="button" value=" 加 " onClick={()=>this.add()} />
     </div>
   )    
}

 //容器组件对外开放时,(把redux里的state转到props) 
export default connect((state)=>{
  return {
    count :state.count    
  }
})(App);
 

在react-redux里,把组件进行拆分(容器组件和UI组件)

容器组件:处理业务逻辑,有状态(在redux里存放)组件,
UI组件:只做展示,就是无状态组件

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值