React react-redux的使用
由于redux是一个独立的库,我们如果需要想在react中更方便的使用redux,推荐使用react-redux这个库,当然不使用这个库也是可以的,那么你需要自己维护全局state,并且订阅state的更新,去修改视图,而react-redux已经帮我们做好了这些东西,并且提供了一些api可以使用。
安装
首先我们需要安装redux和react-redux
yarn add redux react-redux
定义store
然后我们需要定义store,我们在这里创建一个姓名和年龄的store,并combineReducers,一般存放在单独的文件中,我们放在store.js中
import { createStore, combineReducers } from 'redux'
const ageReducer = (state = {age: 18}, action) => {
  switch (action.type) {
    case 'increment':
      return {...state, age: state.age + 1}
    case 'decrement':
      return {...state, age: state.age - 1}
    default:
      return state
  }
}
const nameReducer = (state = {name: 'jerry'}, action) => {
  switch (action.type) {
    case 'changeName':
      return {...state, name: action.payload.name}    
    default:
      return state
  }
}
const reducers = combineReducers({ageReducer, nameReducer});
const store = createStore(reducers)
export default store
在react中使用
如何将store和react app之间关联起来呢?这就是react-redux的作用,react-redux提供一个Provider组件,将store绑定到组件,一般我们直接绑定到根组件app上,那么子组件中也可以使用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')
);
在组件中使用
store已经可用,但是在组件中要怎么使用,怎么拿到state,又怎么派发action呢?此时需要使用到react-redux提供的connect高阶组件了,这个是可选的,如果你的组件不需要访问store,就可以不使用connect。
来看看具体用法,假设我们有一个组件User.js,渲染姓名和年龄,注意我们使用的时候不提供name和age`` props
import React from 'react'
class User extends React.Component {
    constructor(props){
        super(props)
    }
    render() {
        return <div>
            姓名: {this.props.name}
            <br />
            年龄: {this.props.age}
        </div>
    }
}
export default User
此时肯定是渲染不出来的,因为我们还没有使用connect连接store,<User />上也没有提供相应的props。
现在我们来连接store,就使用到了connect高阶组件,connect接收两个函数作为参数,我们习惯性的称之为mapStateToProps和mapDispatchToProps,顾名思义,就是把state和dispatch映射到props上,使我们的组件可以通过props属性访问state和dispatch,通过dispatch,就可以派发action了。第二个函数是可选的,如果不传,会直接把dispatch映射到组件的props上
mapStateToProps
我们先来看看mapStateToProps怎么用,我们打印一下state,看看是什么东西
import React from 'react'
import { connect } from 'react-redux'
class User extends React.Component {
    constructor(props){
        super(props)
    }
    render() {
        return <div>
            姓名: {this.props.name}
            <br />
            年龄: {this.props.age}
        </div>
    }
}
const mapStateToProps = (state) => {
    console.log(state)
}
const ConnectedUser = connect(mapStateToProps)(User)
export default ConnectedUser

可以看到,就是state对象,注意我们是使用combineReducers过的,是有key的,具体可以查看Redux combineReducers
从上图报错也可以看出来,mapStateToProps需要返回一个纯对象,因此我们可以拿到所有的state后,只挑选出对自己有用的,如果都需要或者图方便,直接返回{...state}即可
// ...
const mapStateToProps = (state) => {
    return {...state}
}
// ...
此时我们就可以在组件的props属性上获取到state了,渲染的时候要根据state的结构进行渲染,因此上面的this.props.name和this.props.age需要修改一下
import React from 'react'
import { connect } from 'react-redux'
class User extends React.Component {
    constructor(props){
        super(props)
    }
    render() {
        console.log(this.props)
        return <div>
            姓名: {this.props.nameReducer.name}
            <br />
            年龄: {this.props.ageReducer.age}
        </div>
    }
}
const mapStateToProps = (state) => {
    console.log(state)
    return {...state}
}
const ConnectedUser = connect(mapStateToProps)(User)
export default ConnectedUser

实际上,如果我们的组件有自己的props,例如有一个性别属性<User gender="male" />,mapStateToProp函数还接受第二个参数,可以获取到组件自己的属性,如果只传了state参数,会自动进行合并,不幸的是,如果组件自身的props和state的属性有重名的,组件自身的属性将会丢失,因此对于这种情况,mapStateToProp函数让你接受第二个参数,就是组件自身的props,具体保留哪个,我们可以自行指定。
例如我们是组件以自身的属性优先,就可以直接return { ...state, ...props },此时组件自身的值总是会覆盖state的值
例如这样使用组件<User ageReducer="male" />
import React from 'react'
import { connect } from 'react-redux'
class User extends React.Component {
    constructor(props){
        super(props)
    }
    render() {
        console.log(this.props)
        return <div>
            姓名: {this.props.nameReducer.name}
            <br />
            年龄: {this.props.ageReducer.age}
        </div>
    }
}
const mapStateToProps = (state, props) => {
    console.log(state)
    return {...state, ...props}
}
const ConnectedUser = connect(mapStateToProps)(User)
export default ConnectedUser

看完记得记得把
<User ageReducer="male" />还原
仅在需要时返回新的对象引用
我们从store中取出的state还可以结合组件自身props进行一些计算操作,例如排序,复杂计算等,但是如果非常耗时,可能会影响性能.
react-redux 内部实现了shouldComponentUpdate方法以便在组件用到的数据发生变化后能够精确地重新渲染。默认地,react-redux使用===对mapStateToProps返回的对象的每一个字段逐一对比,以判断内容是否发生了改变。
react-redux进行浅比较来检查mapStateToProps的结果是否改变了。返回一个新对象或数组引用十分容易操作,但会造成你的组件在数据没变的情况下也重新渲染。
我们建议将所有复杂的查找和计算数据的方法封装到selector中。此外,你今后可以通过使用Reselect编写“memoized” selectors来跳过不必要的工作从而优化性能。还可以将复杂计算放到组件内部去做计算,使用useMeme或useCallback进行优化
mapDispatchToProps
在来看看如何dispatch一个action,上面看到,dispatch函数已经是默认被传递到组件的props属性上了,如果不做操作,可以直接使用this.props.dispatch派发一个action
例如:
import React from 'react'
import { connect } from 'react-redux'
class User extends React.Component {
    constructor(props){
        super(props)
    }
    handleIncrementClick = () => {
        this.props.dispatch({type: 'increment'})
    }
    handleDecrementClick = () => {
        this.props.dispatch({type: 'decrement'})
    }
    render() {
        console.log(this.props)
        return <div>
            姓名: {this.props.nameReducer.name}
            <br />
            年龄: {this.props.ageReducer.age}
            <br />
            <button onClick={this.handleIncrementClick}>+</button>
            <button onClick={this.handleDecrementClick}>-</button>
        </div>
    }
}
const mapStateToProps = (state, props) => {
    console.log(state)
    return {...state, ...props}
}
const ConnectedUser = connect(mapStateToProps)(User)
export default ConnectedUser

这样做没问题,但是直接在组件内部派发action,组件里面可能很多地方都需要dispatch,分散在组件的各个地方,代码维护起来就会十分困难,如果在组件内部还要继续向子组件里面传递dispatch,再在子组件内部派发action,就非常混乱了,并且暴露了父层的dispatch给子组件了,子组件如果内部还有自己的connect,就会非常混乱。
此时借助connect的第二个参数,我们可以在mapDispatchToProps里面集中定义组件的需要的dispatch方法,再传给组件,这样集中管理起来代码维护起来成本就降低很多了。
怎么用呢?mapDispatchToProps接收dispatch作为参数,返回一个派发action的函数对象,一旦定义了这个函数,dispatch就不会默认传给组件了,需要自己定义。
import React from 'react'
import { connect } from 'react-redux'
class User extends React.Component {
    constructor(props){
        super(props)
    }
    handleIncrementClick = () => {
        this.props.ageIncrement()
    }
    handleDecrementClick = () => {
        this.props.ageDecrement()
    }
    handleNameChange = () => {
        this.props.changeName({name: 'tom'})
    }
    render() {
        console.log(this.props)
        return <div>
            姓名: {this.props.nameReducer.name}
            <br />
            年龄: {this.props.ageReducer.age}
            <br />
            <button onClick={this.handleIncrementClick}>+</button>
            <button onClick={this.handleDecrementClick}>-</button>
            <button onClick={this.handleNameChange}>改变名字</button>
        </div>
    }
}
const mapStateToProps = (state, props) => {
    console.log(state)
    return {...state, ...props}
}
const mapDispatchToProps = (dispatch) => {
    return {
        ageIncrement: payload => dispatch({type: 'increment', payload}),
        ageDecrement: payload => dispatch({type: 'decrement', payload}),
        changeName: payload => dispatch({type: 'changeName', payload}),
    }
}
const ConnectedUser = connect(mapStateToProps, mapDispatchToProps)(User)
export default ConnectedUser
这样的话,我们自己定义的派发action函数ageIncrement、ageDecrement、changeName就会放到组件的props属性上了
mapDispatchToProps简写形式
使用上述方式定义mapDispatchToProps看起来有点累赘,react-redux也帮我们想到了,在connect函数的第二个参数上,可以直接传递一个action creator函数的数组,然后使用bindActionCreators API来帮我们进行处理,bindActionCreators的使用具体请看Redux bindActionCreators
我们对mapDispatchToProps进行改写,这样看起来好多了,功能也没有受到影响。
// ...
// const mapDispatchToProps = (dispatch) => {
//     return {
//         ageIncrement: payload => dispatch({type: 'increment', payload}),
//         ageDecrement: payload => dispatch({type: 'decrement', payload}),
//         changeName: payload => dispatch({type: 'changeName', payload}),
//     }
// }
const ConnectedUser = connect(mapStateToProps, {
    ageIncrement(payload) {
        return {type: 'increment', payload}
    },
    ageDecrement(payload) {
        return {type: 'decrement', payload}
    },
    changeName(payload) {
        return {type: 'changeName', payload}
    }
})(User)
export default ConnectedUser
react-redux的基本使用就这些了,如果想要继续深入学习,可以看一下它的源码,和中文文档
现在都流行hooks,react-redux也实现了hooks,留坑,有时间再更新吧。
用例源码
store.js
import { createStore, combineReducers } from 'redux'
const ageReducer = (state = {age: 18}, action) => {
  switch (action.type) {
    case 'increment':
      return {...state, age: state.age + 1}
    case 'decrement':
      return {...state, age: state.age - 1}
    default:
      return state
  }
}
const nameReducer = (state = {name: 'jerry'}, action) => {
  switch (action.type) {
    case 'changeName':
      return {...state, name: action.payload.name}    
    default:
      return state
  }
}
const reducers = combineReducers({ageReducer, nameReducer});
const store = createStore(reducers)
export default store
index.js
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, { Profiler } from 'react';
import SagaTest from './components/redux/storeTest'
function App () {
  return (
    <div className="App">
      <SagaTest gender="male"></SagaTest>
    </div>
  );
}
export default App;
src/components/redux/storeTest.js
import React from 'react'
import { connect } from 'react-redux'
class User extends React.Component {
    constructor(props){
        super(props)
    }
    handleIncrementClick = () => {
        this.props.ageIncrement()
    }
    handleDecrementClick = () => {
        this.props.ageDecrement()
    }
    handleNameChange = () => {
        this.props.changeName({name: 'tom'})
    }
    render() {
        console.log(this.props)
        return <div>
            姓名: {this.props.nameReducer.name}
            <br />
            年龄: {this.props.ageReducer.age}
            <br />
            <button onClick={this.handleIncrementClick}>+</button>
            <button onClick={this.handleDecrementClick}>-</button>
            <button onClick={this.handleNameChange}>改变名字</button>
        </div>
    }
}
const mapStateToProps = (state, props) => {
    console.log(state)
    return {...state, ...props}
}
// const mapDispatchToProps = (dispatch) => {
//     return {
//         ageIncrement: payload => dispatch({type: 'increment', payload}),
//         ageDecrement: payload => dispatch({type: 'decrement', payload}),
//         changeName: payload => dispatch({type: 'changeName', payload}),
//     }
// }
const ConnectedUser = connect(mapStateToProps, {
    ageIncrement(payload) {
        return {type: 'increment', payload}
    },
    ageDecrement(payload) {
        return {type: 'decrement', payload}
    },
    changeName(payload) {
        return {type: 'changeName', payload}
    }
})(User)
export default ConnectedUser
      
       转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com