欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

redux

程序员文章站 2022-04-14 17:04:10
redux 记录一下 redux 的一些用法,如果想学习 redux,建议看 "官方文档" ,另外推荐一本 "huzidaha" 写的 "react小书" ,里面讲解了一些 react 和 redux 的原理。 start 运行如下命令,不了解 npx 的,可以看一下 "阮一峰的文章" 。 然后安装 ......

redux

记录一下 redux 的一些用法,如果想学习 redux,建议看,另外推荐一本写的,里面讲解了一些 react 和 redux 的原理。

start


运行如下命令,不了解 npx 的,可以看一下。

// 脚手架创建项目
npx create-react-app redux-test
// 进入文件夹
cd redux-test
// 启动 react
npm run start

然后安装 redux:

npm i -s redux react-redux

接着把 src 下的文件都删掉几个,只剩两个文件:

src/
|--index.js
|--serviceworker.js

redux and react-redux


打开上面留下的 index.js,删掉里面的代码,敲下自己的代码,然后刷新网页。

// index.js
import react, { component } from 'react'
import reactdom from 'react-dom'
import * as serviceworker from './serviceworker'
import { createstore } from 'redux'
import { connect, provider  } from 'react-redux'

// actiontypes
const num_add = 'num_add';

// actioncreator
function addnum () {
  return {
    type: num_add
  }
}

// reducer
const initialstate = {
  num: 0
}

function counter (state = initialstate, action) {
  switch  (action.type) {
    case num_add:
      return {
        num: state.num + 1
      }
    default:
      return state
  }
}

// store
const store = createstore(counter)

// app
class app extends component {
  render() {
    return (
      <div>
        {this.props.num}
        <button onclick={this.props.onclickadd}>add</button>
      </div>
    );
  }
}

// connect(mapstatetoprops, mapdispatchtoprops)(component)
const mapstatetoprops = state => {
  return {
    num: state.num
  }
}

const mapdispatchtoprops = dispatch => {
  return {
    onclickadd: () => {
      dispatch(addnum())
    }
  }
}

app = connect(mapstatetoprops, mapdispatchtoprops)(app)

// provider 传入 store
reactdom.render(
<provider store={store}>
  <app />
</provider>, document.getelementbyid('root'));

serviceworker.unregister();

mapdispatchtoprops


connect中的mapdispatchtoprops可以是一个函数,也可以是一个对象,函数写法如上。

mapdispatchtoprops是一个对象时,redux 会把它里面的属性作为actioncreator交给dispatch使用,简单来说,就是 redux 帮你把对象里面的属性封装为函数型的mapdispatchtoprops,写法如下:

// index.js
...
// app
class app extends component {
  render() {
    return (
      <div>
        {this.props.num}
        // 这里也进行了修改 
        // onclickadd -> addnum
        <button onclick={this.props.addnum}>add</button>
      </div>
    );
  }
}
...
// connect
app = connect( 
  state => state, 
  { addnum }
)(app)
...

@connect


connect可以使用装饰器的写法。

// 装饰器的 babel 插件
npm i -s @babel/plugin-proposal-decorators

然后进行配置 plugin,这里有两种配置方法:

  1. 使用 create-react-app 的配置

    // 暴露配置
    npm run eject
    // 会进行确认
    are you sure you want to eject? this action is permanent.(y/n) y

    然后会看到文件夹内多了一些东西,打开项目根路径下的 package.json 文件,找到 babel 配置项:

    // package.json
    ...
    "babel": {
        "presets": [
          "react-app"
        ],
        "plugins": ["@babel/plugin-proposal-decorators", { "legacy": true }]
      },
    ...
  2. 使用独立文件配置 babel:

    打开项目根路径下的 package.json 文件,找到 babel 配置项,将他删掉:

    // package.json
    ...
    // 把这个删掉
    "babel": {
        "presets": [
          "react-app"
        ],
      },
    ...

    然后在项目根目录创建.babelrc文件。

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

配置完 babel 之后,打开 src 下的 index.js 进行修改:

// index.js
...
// app
// 装饰器写法
@connect(
  state => state, 
  { addnum }
)
class app extends component {
  render() {
    return (
      <div>
        {this.props.num}
        <button onclick={this.props.addnum}>add</button>
      </div>
    );
  }
}
...

注意:对于装饰器的支持只是实验性的,未来可能会改动。

redux-thunk


可以使用中间件 redux-thunk 进行异步操作,它可以让actioncreator不返回action对象,而是返回一个函数,可以在函数内封装逻辑。

function incrementifodd() {
  // 接收两个参数
  // getstate() 可以拿到 store 中的 state
  return (dispatch, getstate) => {
    const { counter } = getstate();
    if (counter % 2 === 0) {
      return;
    }
    dispatch(increment());
  };
}

首先还是安装:

npm i -s redux-thunk

然后打开 index.js 进行修改:

// index.js
...
import {createstore, applymiddleware} from 'redux'
import thunk from 'redux-thunk'
...
// store
const store = createstore(counter, applymiddleware(thunk))
...

异步操作有很多,这里将 num 的增加推迟为 1s 后才进行:

// index.js
// actioncreator
function addnum () {
  return {
    type: num_add
  }
}

function addnumasync () {
  return dispatch => {
    settimeout(() => {
      dispatch({type: num_add})
    }, 1000)
  }
}

// app
@connect(
  state => state, 
  { addnum, addnumasync }
)
class app extends component {
  render() {
    return (
      <div>
        {this.props.num}
        <button onclick={this.props.addnum}>add</button>
        <button onclick={this.props.addnumasync}>add after 1s</button>
      </div>
    );
  }
}

combinereducers


当你有多个 reducer 时,可以使用combinereducers,进行合并。

import { createstore, combinereducers } from 'redux'

const allreducer = combinereducers({
  reducerone,
  reducerstwo
})

store = createstore(allreducer)

bindactioncreators


把 actioncreator 传到一个子组件中,却不想让这个组件觉察到 redux 的存在,而且不希望把 dispatch 或 store 传给它时,可以使用bindactioncreators

这里使用官方文档的例子:

// todoactioncreators.js
export function addtodo(text) {
  return {
    type: 'add_todo',
    text
  };
}

export function removetodo(id) {
  return {
    type: 'remove_todo',
    id
  };
}
// somecomponent.js
import { component } from 'react';
import { bindactioncreators } from 'redux';
import { connect } from 'react-redux';

import * as todoactioncreators from './todoactioncreators';
console.log(todoactioncreators);
// {
//   addtodo: function,
//   removetodo: function
// }

class todolistcontainer extends component {
  constructor(props) { 
    super(props);

    const {dispatch} = props;

    // 这是一个很好的 bindactioncreators 的使用示例:
    // 你想让你的子组件完全不感知 redux 的存在。
    // 我们在这里对 action creator 绑定 dispatch 方法,
    // 以便稍后将其传给子组件。

    this.boundactioncreators = bindactioncreators(todoactioncreators, dispatch);
    console.log(this.boundactioncreators);
    // {
    //   addtodo: function,
    //   removetodo: function
    // }
  }

  componentdidmount() {
    // 由 react-redux 注入的 dispatch:
    let { dispatch } = this.props;

    // 注意:这样是行不通的:
    // todoactioncreators.addtodo('use redux')

    // 你只是调用了创建 action 的方法。
    // 你必须要同时 dispatch action。

    // 这样做是可行的:
    let action = todoactioncreators.addtodo('use redux');
    dispatch(action);
  }

  render() {
    // 由 react-redux 注入的 todos:
    let { todos } = this.props;

    return <todolist todos={todos} {...this.boundactioncreators} />;

    // 另一替代 bindactioncreators 的做法是
    // 直接把 dispatch 函数当作 prop 传递给子组件,但这时你的子组件需要
    // 引入 action creator 并且感知它们

    // return <todolist todos={todos} dispatch={dispatch} />;
  }
}

export default connect(state => ({ todos: state.todos }))(todolistcontainer)

备注


个人学习 redux 的感受,看,不如动手去敲。