通过 todoList 搞懂 redux

React中的数据传递是从根节点开始,一级一级的向下传递, 相邻较远的组件之间的数据进行交流 就需要一个漫长的传递数据的过程, 为了解决这个问题, 便有了 Redux。

简而言之, Redux 是一个状态管理工具。 Rudux中有一个存储数据的 store, 这个 store 是独立于React组件之外的,每一个组件都可以直接拿到和修改store中的数据, 就不需要把数据传来传去了。

我们通过 一个 todoList的demo 来 弄懂 redux。

在开始之前, 首先 先使用 creat-react-app 初始化一个react项目。 为了方便使用, 项目中的组件直接使用 ant design 中组件。

首先, 安装 redux

yarn add redux react-redux antd -D
复制代码

基础

action

redux 中的 store 是只读的。 要想修改 store中的值,只能通过触发 action 的方式来实现。 action 本质是一个 js 对象, action 内使用 type 字段来表示 将要执行动作,可以在reducer中针对不同type执行相应的数据操作。

action 创建函数

action 创建函数 是 生成一个 action的方法。

export const ADD_TODO = 'ADD_TODO';
export const DEL_TODO = 'DEL_TODO';

export function addTodo(text) {
    return {
        type: ADD_TODO,
        text
    }
}

export function delTodo(index) {
    return {
        type: DEL_TODO,
        index
    }
}

复制代码

在 Rudux 中, 我们可以调用 store.dispatch() 方法, 触发action。

在 React 中, 可以使用 react-redux 中的 connect() 方法来使用。 bindActionCreators() 可以自动把多个 action 创建函数 绑定到 dispatch() 方法上。

reducer

reducer 是一个纯函数, 接受 旧的 state 和 action, 最后返回修改后的新 state。

注: 1. 不要直接修改 state。 更新state时, 可以采用 Object.assign({}, {...newState}) 的方式。 2. 在 default 情况下返回旧的 state。遇到未知的 action 时,一定要返回旧的 state。

reducer/list.js

import {
    ADD_TODO,
    DEL_TODO,
} from '../actions';

export function list(state = [], action) {
    switch (action.type) {
        case ADD_TODO: {
            const { text } = action;
            return [...state, text];
        }
        case DEL_TODO: {
            const { index } = action;
            let newState = [...state];
            newState.splice(index, 1);
            return newState;
        }
        default: {
            return state;
        }
    }
}
复制代码

reducer/index.js

import { combineReducers } from 'redux'
import list from './list';

const app = combineReducers({
    list,
})

export default app;
复制代码
store

store 就是我们当前项目的仓库, 我们可以在 store存储 我们项目中的用到的所有数据。 Redux 应用中只有一个单一的store。 如果需要对数据进行拆分, 可以把数据存储在多个reducer中, 然后使用 combineReducers() 将多个reducer 合并为一个。

所有的容器组件都可以访问 Redux Store, 一种方式是以 props 的形式传入 容器组件中, 但这样使用比较麻烦。 react 中 我们可以使用 , 这样 我们所有的容器组件都可以访问store,不需要通过 props 的方式进行传递。

在react中, 容器组减可以使用 connect() 方法访问 store。使用 connect() 时, 使用mapStateToProps这个函数来指定当前 Redux store state 中需要映射到展示组件的 props 中的数据, 使用mapDispatchToProps() 方法接收 dispatch() 方法并返回期望注入到展示组件的 props 中的回调方法。

import { connect } from 'react-redux';
import { addTodo } from 'action';

class Todo extends Component {
    // ...
}

export default connect(
  (state) => {
      return {
          list: state.list,
      }
  },
  {
    addTodo,
  }
)(Todo)
复制代码
redux 调试工具

redux有很多 第三方调试工具, 我们这里使用redux-devtools-extension.

使用步骤:

  1. 首先,在Chrome中安装Redux Devtools扩展。
  2. 用npm或yarn安装redux-devtools-extension包。

index.js

// ...
import { composeWithDevTools } from 'redux-devtools-extension';

const store = createStore(app, composeWithDevTools());
复制代码

代码

项目结构

├── README.md ├── package.json ├── src │   ├── App.css │   ├── App.js │   ├── App.test.js │   ├── actions │   │   └── index.js │   ├── components │   │   ├── Footer │   │   │   ├── index.js │   │   │   └── style.css │   │   └── Todo │   │   ├── index.js │   │   └── style.css │   ├── index.css │   ├── index.js │   ├── logo.svg │   ├── reducer │   │   ├── index.js │   │   └── list.js │   └── registerServiceWorker.js └── yarn.lock

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import app from './reducer';
import registerServiceWorker from './registerServiceWorker';
import App from './App';
import './index.css';

const store = createStore(app, composeWithDevTools());

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

复制代码
App.js
import React, { Component } from 'react';
import Footer from './components/Footer';
import Todo from './components/Todo';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <div className="App-intro">
          <Footer />
          <Todo />
        </div>
      </div>
    );
  }
}

export default App;

复制代码
action/index.js
export const ADD_TODO = 'ADD_TODO';
export const DEL_TODO = 'DEL_TODO';

export function addTodo(text) {
    return {
        type: ADD_TODO,
        text
    }
}

export function delTodo(index) {
    return {
        type: DEL_TODO,
        index
    }
}

复制代码
reducer/index.js
import { combineReducers } from 'redux'
import list from './list';

const app = combineReducers({
    list,
})

export default app;

复制代码
reducer/list.js
import {
    ADD_TODO,
    DEL_TODO,
} from '../actions';

export default function list(state = [], action) {
    switch (action.type) {
        case ADD_TODO: {
            const { text } = action;
            return [...state, text];
        }
        case DEL_TODO: {
            const { index } = action;
            let newState = [...state];
            newState.splice(index, 1);
            return newState;
        }
        default: {
            return state;
        }
    }
}
复制代码
component/Footer/index.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Input, message } from 'antd';
import { addTodo } from '../../actions';
import "./style.css";

const Search = Input.Search;

class Footer extends Component {
    constructor(props) {
        super(props);
        this.state = {
            inputValue: '',
        }
    }

    handleChange = (e) => {
        const inputValue = e.target.value;
        this.setState({
            inputValue
        })
    }

    addTodo = () => {
        const { inputValue } = this.state;
        if (String(inputValue)) {
            this.props.addTodo(inputValue);
            this.setState({
                inputValue: '',
            })
        } else {
            message.error('todo is require');
        }
    }
 
    render() {
        const { inputValue } = this.state;
        return (
            <div className="footer">
                <Search
                    value={inputValue}
                    placeholder="add a todo"
                    onChange={this.handleChange}
                    onSearch={this.addTodo}
                    enterButton="ADD"
                />
            </div>
        );
    }
}

export default connect(
    () => { return {} },{
        addTodo
    }
  )(Footer);

复制代码
component/Todo/index.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Button } from 'antd';
import { delTodo } from '../../actions';
import "./style.css";

class Todo extends Component {
    constructor(props) {
        super(props);
    }

    delTodo = (index) => {
        this.props.delTodo(index);
    }
 
    render() {
        const { list } = this.props;

        if (!list || !list.length) {
            return null;
        }
        return (
            <div className="todo">
                {
                    list.map((item, index) => {
                        return (
                            <div key={index} className="todoItem">
                                {item}
                                <Button onClick={() => this.delTodo(index)}>Del</Button>
                            </div>
                        )
                    })
                }
            </div>
        );
    }
}

export default connect(
    (state) => { return {
        list: state.list,
    } },{
        delTodo
    }
  )(Todo);

复制代码

转载于:https://juejin.im/post/5ba9e8f16fb9a05d0a0530c8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值