当前位置:   article > 正文

React入门实战:使用ReactRedux进行状态管理_react-redux使用

react-redux使用

1.背景介绍

什么是React?

React是一个由Facebook开发并开源的JS框架,用于构建用户界面。它主要用来创建可复用组件、处理用户交互和管理数据。它的特点包括快速渲染、声明式编程、组件化开发和React Native支持。它的架构由两大部分组成:

  • JSX(JavaScript XML) - 使用XML语法编写组件的代码,通过编译器转换成JavaScript代码;
  • Virtual DOM (虚拟DOM) - 在浏览器中使用一个轻量级、跨平台的对象表示法来表示真实的DOM树,使得React可以最大限度地减少页面的重绘次数,提高性能;

    为什么要使用React?

    React在Facebook内部已经应用了十多年时间,目前被认为是前端界最火热的框架之一。其优秀的性能表现、丰富的组件库和生态圈让其成为前端领域里最流行的工具。由于React具有简单易上手、学习曲线平滑、灵活性强等特性,因此越来越多的人开始尝试学习或使用React。其中就包括我司CTO、技术经理和技术专家等等。但即使如此,对于新人来说,掌握React也是一件非常困难的事情。 那么,为什么还要费力气阅读这篇文章呢?其实这个问题很简单。因为很多人不了解React的工作流程,对于数据的管理没有清晰的认识。他们往往会觉得在React中管理数据非常麻烦,甚至于错误率也高。而本文将带领大家一步步走进React-Redux的世界,从基础知识到实际应用,带领大家建立起全面的React状态管理体系。

    2.核心概念与联系

    首先,我们需要对一些相关的概念有一个基本的了解。下面是我们所需要了解的核心概念:

    Redux(Reactive State Container)

    Redux是一个JavaScript库,它帮助你构建可预测的状态容器,你可以把它看作是一个轻量级的单向数据流,应用中的所有状态都保存在一个共享的存储空间里,而且只允许修改的方式是发送一个 action 到 store。我们可以使用 reducer 函数根据传入的 action 更新 state 。Reducer 函数接收旧的 state 和 action,返回新的 state。这样做的好处就是易于理解和测试。Redux提供了一个 Store 的概念,store 保存着完整的应用的 state 。React-Redux 提供了 Provider 和 connect() 方法,用于从 Redux store 获取 state 并注入到 React 组件里。这样,React 组件可以通过 props 来获取 state ,并通过调用 actions 来触发更新 state 。

Action

Action 是 Redux 中的基本组成单位,它描述应用程序所需执行的动作,并且可以携带一定的数据。在 Redux 中,每个 action 都是 JavaScript 对象,必需有 type 属性。一般情况下,action creator 会生成一个 action 对象。比如,如果需要创建一个 action,用来改变 counter 的值,可以定义如下:

const changeCounter = value => {
  return {
    type: 'CHANGE_COUNTER',
    payload: value
  };
};
  • 1
  • 2
  • 3
  • 4
  • 5

Reducer

Reducer 函数是 Redux 中最重要的部分之一,它是个纯函数,接收先前的 state 和 action,返回新的 state。Reducer 函数根据 action 的类型决定如何更新 state 。比如,下面是改变 counter 的 reducer 函数:

const initialState = { count: 0 };

function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return {...state, count: state.count + 1 };

    case 'DECREMENT':
      return {...state, count: state.count - 1 };

    default:
      return state;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Dispatch

Dispatch 是 Redux API 中最常用的方法之一,它可以触发 action 的生成和传递,例如:

import { increment, decrement } from './actions';

dispatch(increment()); // dispatch the INCREMENT action
dispatch(decrement()); // dispatch the DECREMENT action
  • 1
  • 2
  • 3

Middleware

Middleware 可以理解为拦截 Redux 的 action 执行过程,比如日志记录、异步请求、异常处理等。中间件通常是实现 Redux 插件功能的一种方式。官方提供的三个中间件分别是 thunk、promise middleware 和 logger。Thunk 是一个中间件,用来实现 Redux 中间件的异步操作。Promise middleware 是另一个中间件,用来处理异步 Promise 请求。Logger 是第三种中间件,用来打印 Redux 的日志。

Component

React组件是视图层的核心构建模块,它负责完成渲染和逻辑交互。每个 React 应用都由不同类型的组件组合而成,这些组件之间又存在依赖关系。组件的 Props 和 State 用来维护组件的局部和全局状态。Props 是父组件向子组件传递数据的方法,State 则用来在组件内记录数据的变化。

State and props

State 和 Props 是 React 中两个最基本的概念,它们是用来管理组件状态和配置信息的。State 代表的是当前组件内部数据的状态,它应该是私有的,只能在组件自身使用,外部无法直接访问或修改。Props 是父组件传递给子组件的数据,它是父组件对子组件的约束条件,外部也可以访问并修改。除此之外,还有其他几个关于状态的概念,如局部状态(Local State)、共享状态(Shared State)等。

3.核心算法原理和具体操作步骤以及数学模型公式详细讲解

一、项目结构

项目结构按照 Redux 规范一般分为以下几部分: 1.Actions 文件夹:存放所有的 action 创建函数文件。 2.Reducers 文件夹:存放所有的 reducer 函数文件。 3.Store 文件夹:存放 Redux 配置文件。 4.Components 文件夹:存放 UI 组件。 5.Containers 文件夹:用于连接 Redux 数据和 UI 组件的文件。 6.Routes 文件夹:用于定义路由规则。

二、项目启动

安装依赖包

npm install redux react-redux --save

    创建 actions.js 文件

    在 Actions 文件夹下创建 actions.js 文件,用于定义 action 创建函数。

    export const addTodo = text => ({
      type: 'ADD_TODO',
      text
    });
    
    export const toggleTodo = index => ({
      type: 'TOGGLE_TODO',
      index
    });
    
    // 添加 todos 初始化函数
    export function loadTodos() {
      return async dispatch => {
        try {
          const response = await fetch('https://jsonplaceholder.typicode.com/todos');
          const data = await response.json();
          console.log(data);
    
          dispatch({
            type: 'LOAD_TODOS',
            data: data
          });
        } catch (error) {
          console.error(error);
        }
      };
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    创建 reducers.js 文件

    在 Reducers 文件夹下创建 reducers.js 文件,用于定义 reducer 函数。

    const initialState = {
      todos: [],
      visibilityFilter: 'SHOW_ALL'
    };
    
    const todoApp = (state = initialState, action) => {
      switch (action.type) {
        case 'ADD_TODO':
          return {
            todos: [...state.todos, {
              text: action.text,
              completed: false
            }],
            visibilityFilter: state.visibilityFilter
          };
    
        case 'TOGGLE_TODO':
          let newTodos = [...state.todos];
          newTodos[action.index].completed =!newTodos[action.index].completed;
    
          return {
            todos: newTodos,
            visibilityFilter: state.visibilityFilter
          };
    
        case 'SET_VISIBILITY_FILTER':
          return {
            todos: state.todos,
            visibilityFilter: action.filter
          };
    
        case 'LOAD_TODOS':
          return {
            todos: action.data,
            visibilityFilter: state.visibilityFilter
          };
    
        default:
          return state;
      }
    };
    
    export default todoApp;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    创建 store.js 文件

    在 Store 文件夹下创建 store.js 文件,用于定义 Redux 配置。

    import { createStore } from'redux';
    import rootReducer from '../reducers/rootReducer';
    
    const store = createStore(rootReducer);
    
    export default store;
    • 1
    • 2
    • 3
    • 4
    • 5

    创建 containers 文件夹

    创建 Containers 文件夹,用于存放连接 Redux 数据和 UI 组件的文件。

    创建 TodosListContainer.js 文件

    在 Containers 文件夹下创建 TodosListContainer.js 文件,用于连接 Redux 数据和 UI 组件。

    import { connect } from'react-redux';
    import { setVisibilityFilter, toggleTodo, addTodo } from '../actions';
    import TodoList from '../../components/TodoList';
    
    const mapStateToProps = state => {
      return {
        todos: state.todoApp.todos,
        visibilityFilter: state.todoApp.visibilityFilter
      };
    };
    
    const mapDispatchToProps = dispatch => {
      return {
        onSetFilter: filter => dispatch(setVisibilityFilter(filter)),
        onToggleTodo: index => dispatch(toggleTodo(index)),
        onAddTodo: text => dispatch(addTodo(text))
      };
    };
    
    const VisibleTodoList = connect(mapStateToProps, mapDispatchToProps)(TodoList);
    
    export default VisibleTodoList;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    创建 components 文件夹

    创建 Components 文件夹,用于存放 UI 组件。

    创建 TodoTextInput.js 文件

    在 Components 文件夹下创建 TodoTextInput.js 文件,用于输入添加 ToDo 任务。

    import React from'react';
    
    class TodoTextInput extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          text: ''
        };
      }
    
      handleChange = e => {
        this.setState({ text: e.target.value });
      };
    
      handleSubmit = e => {
        e.preventDefault();
        if (!this.state.text.trim()) {
          return;
        }
    
        this.props.onAddTodo(this.state.text);
        this.setState({ text: '' });
      };
    
      render() {
        return (
          <div>
            <form onSubmit={this.handleSubmit}>
              <input
                placeholder="What needs to be done?"
                autoFocus
                value={this.state.text}
                onChange={this.handleChange}
              />
              <button>{'Add #' + (this.props.todos.length + 1)}</button>
            </form>
          </div>
        );
      }
    }
    
    export default TodoTextInput;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    创建 VisibilityFilters.js 文件

    在 Components 文件夹下创建 VisibilityFilters.js 文件,用于选择展示哪些任务。

    import React from'react';
    import PropTypes from 'prop-types';
    
    const filters = {
      SHOW_ALL: 'Show All',
      SHOW_COMPLETED: 'Show Completed',
      SHOW_ACTIVE: 'Show Active'
    };
    
    class VisibilityFilters extends React.Component {
      static propTypes = {
        currentFilter: PropTypes.string.isRequired,
        onFilterChange: PropTypes.func.isRequired
      };
    
      handleClick = filter => {
        this.props.onFilterChange(filter);
      };
    
      render() {
        return (
          <ul className="filters">
            {Object.keys(filters).map(filterKey => (
              <li key={filterKey}>
                <a
                  href="#"
                  onClick={() => this.handleClick(filterKey)}
                  className={
                    filterKey === this.props.currentFilter
                     ?'selected'
                      : ''
                  }
                >
                  {filters[filterKey]}
                </a>
              </li>
            ))}
          </ul>
        );
      }
    }
    
    export default VisibilityFilters;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    创建 Footer.js 文件

    在 Components 文件夹下创建 Footer.js 文件,用于显示脚部信息。

    import React from'react';
    
    class Footer extends React.Component {
      shouldComponentUpdate() {
        return false;
      }
    
      render() {
        const activeCount = this.props.todos.filter(todo =>!todo.completed).length;
        const itemWord = activeCount!== 1? 'items' : 'item';
    
        return (
          <p>
            Showing {activeCount} of {this.props.todos.length}{' '}
            {itemWord} left
          </p>
        );
      }
    }
    
    export default Footer;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    创建 App.js 文件

    在 Components 文件夹下创建 App.js 文件,用于显示整个应用。

    import React, { useState } from'react';
    import { BrowserRouter as Router, Switch, Route } from'react-router-dom';
    import { addTodo } from '../actions';
    import AddTodo from './AddTodo';
    import VisibleTodoList from './VisibleTodoList';
    import Footer from './Footer';
    
    const inititalTodos = [
      { id: 0, text: 'Use Redux', completed: true },
      { id: 1, text: 'Run a marathon', completed: false },
      { id: 2, text: 'Write a book', completed: false },
      { id: 3, text: 'Buy a TV', completed: true }
    ];
    
    function App() {
      const [todos, setTodos] = useState(inititalTodos);
      const [visibilityFilter, setVisibilityFilter] = useState('SHOW_ALL');
    
      const handleAddTodo = text => {
        const newTodos = [
         ...todos,
          { id: Math.random(), text, completed: false }
        ];
    
        setTodos(newTodos);
      };
    
      const filteredTodos = () => {
        switch (visibilityFilter) {
          case 'SHOW_COMPLETED':
            return todos.filter(todo => todo.completed);
          case 'SHOW_ACTIVE':
            return todos.filter(todo =>!todo.completed);
          default:
            return todos;
        }
      };
    
      return (
        <Router>
          <div>
            <h1>My Todos</h1>
            <Switch>
              <Route path="/" exact component={AddTodo} />
              <Route path="/list" component={VisibleTodoList} />
            </Switch>
            <Footer
              todos={filteredTodos()}
              visibilityFilter={visibilityFilter}
              onFilterChange={setVisibilityFilter}
            />
          </div>
        </Router>
      );
    }
    
    export default App;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    修改根目录下的 index.js 文件

    在根目录下修改 index.js 文件,用于导出 store 对象。

    import React from'react';
    import ReactDOM from'react-dom';
    import './styles/global.css';
    import App from './containers/App';
    import registerServiceWorker from './registerServiceWorker';
    import { createStore } from'redux';
    import { Provider } from'react-redux';
    import rootReducer from './reducers';
    
    const store = createStore(rootReducer);
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
    registerServiceWorker();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    安装 React-redux 相关依赖包

    npm install prop-types react-router-dom --save

      以上安装完毕后,项目的依赖包如下图所示:

      三、项目运行

      在浏览器中查看效果

      运行项目,打开浏览器,输入 http://localhost:3000/ ,即可看到页面效果如下图所示:

      测试功能是否正常

      查看初始化的 ToDos

      我们可以查看初始时页面加载时默认展示的 ToDos 是否正确。

      添加 ToDo 任务

      我们可以在输入框输入文字后点击按钮添加 ToDo 任务。

      删除已完成任务

      我们可以点击圆圈勾选标记的任务后,再点击顶部删除按钮删除已完成任务。

      筛选任务

      我们可以选择展示全部、待办或已办的任务。

      声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/510737
      推荐阅读
      相关标签
        

      闽ICP备14008679号