记录一下redux和@connect装饰器的基本用法

1,374 阅读4分钟

首先第一步利用脚手架创建一个项目,命令是:create-react-app my_redux

接着安装redux,命令:npm install --save redux react-redux redux-thunk

Redux有三大原则

单一数据源

整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

State 是只读的

唯一改变 state 的方法就是触发 actionaction 是一个用于描述已发生事件的普通对象。

使用纯函数来执行修改

为了描述 action 如何改变 state tree ,我们需要编写 reducer

接下来我们删除src下多余文件,只留下app.jsindex.js,创建store文件夹,并创建我们需要的js文件,此时src目录下应该是这些文件

|-- src
|  |-- store
|  |  |-- index.js
|  |  |-- reducer.js
|  |  |-- action.js
|  |-- index.js
|  |-- app.js

以todoList为例

使用 action 来描述所有变化带来的好处是可以清晰地知道应用中到底发生了什么。如果一些东西改变了,就可以知道为什么变。action 就像是描述发生了什么的指示器。
action.js

//把需要操作的方法导出去
export const addTodo = (data) => {
  return dispatch => {
    //告诉reducer类型,reducer根据不同的类型做不同的操作
    dispatch({ type: 'ADD_TODO', data })
  }
}
export const delTodo = (index) => {
  return dispatch => {
    dispatch({type : 'DEL_TODO',index})
  }
}

reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。刚开始你可以只有一个 reducer,随着应用变大,你可以把它拆成多个小的 reducers,分别独立地操作 state tree 的不同部分,因为 reducer 只是函数,你可以控制它们被调用的顺序,传入附加数据,甚至编写可复用的 reducer 来处理一些通用任务。
reduce.js

//创建唯一的初始化state,如果初始化元素比较多可以单独创建state.js方便管理
const initState = {
  todoList : ['早上五点钟起床','锻炼身体一小时','做饭吃饭']
}

//编写reducer根据不同的类型做不同的操作修改state
const reducer = (state = initState,action) =>{
  //state是只读,所以每次修改都返回一个新的state
  let newState = JSON.parse(JSON.stringify(state))
  switch(action.type){
    case 'ADD_TODO':
      newState.todoList.push(action.data);
      return newState;
    case 'DEL_TODO':
      newState.todoList.splice(action.index,1);
      return newState;
    default:
      return newState;
  }
}

export default reducer

创建store实例
store -> index.js

// applyMiddleware: redux通过该函数来使用中间件
// createStore: 用于创建store实例
import {createStore,applyMiddleware} from 'redux'
//thunk中间件可以支持异步action
import thunk from 'redux-thunk'
import reducer from './reducer'
const store = createStore(reducer,applyMiddleware(thunk))
export default store

至此,我们已经完成了所有使用Redux的准备工作,接下来就在React组件中使用Redux
首先编写入口文件index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
//// 引入创建好的store实例
import store from './store'
// Provider是react-redux两个核心工具之一,作用:将store传递到每个项目中的组件中
import {Provider} from 'react-redux'

ReactDOM.render(
  <Provider store={store}>
    {/* 将store作为prop传入,即可使应用中的所有组件使用store */}
    <App />
  </Provider>,
  document.getElementById('root')
);

最后app.js

import React, { Component } from 'react'
// connect方法的作用:将额外的props传递给组件,并返回新的组件,组件在该过程中不会受到影响
import { connect } from 'react-redux'
// 引入action
import {addTodo,delTodo} from './store/action'
class App extends Component {
  state = {
    value : ''
  }
  changeValue = (e) =>{
    this.setState({
      value : e.target.value,
    })
  }
  addTodoFn = () =>{
    //触发addTodo添加备忘的action
    this.props.addTodo(this.state.value)
    this.setState({
      value :''
    })
  }
  delTodoFn = (index) =>{
    //触发delTodo删除备忘的action
    this.props.delTodo(index)
  }
  render() {
    return (
      <div>
        <ol>
          {this.props.todoList.map((item,index)=>(
            <li key={index} onClick={this.delTodoFn.bind(this,index)}>{item}</li>
          ))}
        </ol>
        <input type="text" value={this.state.value} onChange={this.changeValue}/>
        <button onClick={this.addTodoFn}>添加</button>
      </div>
    )
  }
}
// mapStateToProps:将state映射到组件的props中
const mapStateToProps = (state) => ({
  todoList : state.todoList
})
// mapDispatchToProps:将action里的dispatch映射到组件的props中
const mapDispatchToProps = {
  addTodo,
  delTodo
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

@connect装饰器

装饰器可以让代码写起来更顺手,看起来也简洁一点,

  1. 在命令行执行命令 npm run eject。如果报错的信息大概意思是:有些文件未被追踪到,那么直接git add .git commit -m"",或者直接在.gitignore中忽略这些文件(不建议);
  2. 之后package.json中会出现很多依赖建议yarn/npm i 一下,防止网速不好出现丢包现象;
  3. 然后打开package.json文件,找到"babel"开头的一个对象,(一般在最后,),这是原始的样子:
"babel": {
    "presets": [
      "react-app"
    ]
  }

加入另外一项:

"babel": {
    "presets": [
      "react-app"
    ],
    "plugins": [
      [
        "@babel/plugin-proposal-decorators",
        {
          "legacy": true
        }
      ]
    ]
  }

最后重启项目就可以了,一定要重启项目不然可能会报错Support for the experimental syntax 'decorators-legacy' isn't currently enabled (6:1):
然后就可以使用@conncet装饰器了,把app.js可以修改如下:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import {addTodo,delTodo} from './store/action'
//直接在class上一行,接收两个参数,跟之前的写法差不多,第一个参数是需要用的state,第二个参数是需要用的action
@connect(state=>({
  todoList : state.todoList
}),{addTodo,delTodo})
class App extends Component {
  state = {
    value : ''
  }
  changeValue = (e) =>{
    this.setState({
      value : e.target.value,
    })
  }
  addTodoFn = () =>{
    //触发addTodo添加备忘的action
    this.props.addTodo(this.state.value)
    this.setState({
      value :''
    })
  }
  delTodoFn = (index) =>{
    //触发delTodo删除备忘的action
    this.props.delTodo(index)
  }
  render() {
    return (
      <div>
        <ol>
          {this.props.todoList.map((item,index)=>(
            <li key={index} onClick={this.delTodoFn.bind(this,index)}>{item}</li>
          ))}
        </ol>
        <input type="text" value={this.state.value} onChange={this.changeValue}/>
        <button onClick={this.addTodoFn}>添加</button>
      </div>
    )
  }
}
//用@connect装饰器一定要以这种方式导出,不然报错,具体什么原因我还没找到
export default App

调试工具插件

window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
在redux中添加这个插件是在chrome浏览器可以看到state的每一步流程,更加清晰,在store下index.js中添加修改

import {createStore,applyMiddleware,compose} from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducer'
const composeEnhancers =
  typeof window === 'object' &&
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
      // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
    }) : compose;

const enhancer = composeEnhancers(
  applyMiddleware(thunk),
  // other store enhancers if any
);
const store = createStore(reducer, enhancer);
export default store

image.png