返回

以优雅方式开发模块化React + Redux应用

前端

当我们开始一个新的应用的时候,有一件事情是一定要考虑清楚的,因为随着项目的增大,我们需要创建的项目结构和大小都越来越复杂,一个好的代码结构能够给我们省事不少。

本篇会以todo应用为代表进行项目文件的划分,因为每个框架问世的时候都会用todo进行展示。上图就是按角色进行代码的划分。

按照这个思维我们将我们的项目进行代码划分,内容如下:

  • actions.js:包含所有action创建函数,由 redux 调用
  • reducers.js:包含所有reducer函数,由 redux 调用
  • store.js:包含创建仓库的函数,由 redux 调用
  • components:存放所有应用组件
  • containers:存放业务组件
  • helpers:存放各种帮助方法
  • index.js:引入所有文件并启动应用
  • package.json:项目配置信息
  • .gitignore:git忽略文件配置

整个项目就是由这么几个文件组成。下面我们以创建todo应用为例,对上述文件进行详细讲解。

首先我们先看一下actions.js

export const ADD_TODO = 'ADD_TODO'

export const addTodo = (text) => {
  return {
    type: ADD_TODO,
    text
  }
}

这个文件里面定义了一个 action 创建函数 addTodo,它会返回一个对象,这个对象包含一个 type 属性和一个 text 属性。

接下来看一下reducers.js

import { ADD_TODO } from './actions'

const initialState = {
  todos: []
}

export const todoReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
      return {
        ...state,
        todos: [...state.todos, action.text]
      }
    default:
      return state
  }
}

这个文件里面定义了一个 reducer 函数 todoReducer,它会接收一个 state 和一个 action,然后返回一个新的 state。在这个 reducer 函数里面,我们只处理了 ADD_TODO 这个 action,当接收到这个 action 时,我们会在 state 的 todos 数组中添加一个新的元素。

再看一下store.js

import { createStore } from 'redux'
import { todoReducer } from './reducers'

const store = createStore(todoReducer)

export default store

这个文件里面定义了一个创建仓库的函数,它会接收一个 reducer 函数并返回一个仓库。

然后我们看一下components文件夹,这个文件夹里面存放着所有应用组件。

import React from 'react'

const Todo = (props) => {
  return (
    <li>
      {props.text}
    </li>
  )
}

export default Todo

这个文件里面定义了一个组件 Todo,它会渲染一个待办事项。

import React from 'react'

const TodoList = (props) => {
  return (
    <ul>
      {props.todos.map((todo, index) => <Todo key={index} text={todo} />)}
    </ul>
  )
}

export default TodoList

这个文件里面定义了一个组件 TodoList,它会渲染一个待办事项列表。

import React from 'react'

const AddTodo = (props) => {
  return (
    <form onSubmit={props.onSubmit}>
      <input type="text" onChange={props.onChange} />
      <button type="submit">添加</button>
    </form>
  )
}

export default AddTodo

这个文件里面定义了一个组件 AddTodo,它会渲染一个添加待办事项的表单。

最后看一下containers文件夹,这个文件夹里面存放着业务组件。

import React, { useState } from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
import AddTodo from '../components/AddTodo'
import TodoList from '../components/TodoList'

const App = (props) => {
  const [text, setText] = useState('')

  const handleChange = (e) => {
    setText(e.target.value)
  }

  const handleSubmit = (e) => {
    e.preventDefault()
    props.addTodo(text)
    setText('')
  }

  return (
    <div>
      <h1>待办事项列表</h1>
      <AddTodo onSubmit={handleSubmit} onChange={handleChange} />
      <TodoList todos={props.todos} />
    </div>
  )
}

const mapStateToProps = (state) => {
  return {
    todos: state.todos
  }
}

const mapDispatchToProps = {
  addTodo
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

这个文件里面定义了一个业务组件 App,它会渲染整个应用。这个组件里面使用了 react-reduxconnect 函数,这个函数可以把仓库中的 state 和 dispatch 函数注入到组件的 props 中。

最后我们看一下index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './containers/App'
import store from './store'

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

这个文件里面负责引入所有文件并启动应用。

这就是一个完整的 todo 应用的代码结构,希望对大家有所帮助。