React实现Redux实例全解 – ReactJS实战教程

上一章ReactJS实战教程请查看:React Redux架构基本概念解释
在本节中,我们将学习如何在React应用程序中实现Redux,这里,我们提供了一个连接Redux和React的简单示例。
步骤1:创建react项目 使用create-react-app命令创建一个新的react项目。我们选择项目名称:“reactproject”,现在安装Redux和React-Redux。

$ npx create-react-app reactproject $ npm install redux react-redux --save

步骤2: 创建文件和文件夹在此步骤中,我们需要为操作、reducer、组件和容器创建文件夹和文件。创建文件夹和文件后,我们的项目如下图所示。
React实现Redux实例全解 – ReactJS实战教程

文章图片
步骤3:操作action它使用“type”属性通知应该发送到存储的数据,在这个文件夹中,我们将创建两个文件: index.js和index.spec.js。在这里,我们创建了一个action creator,它返回我们的操作并为每个创建的项设置id。
Index.js
let nextTodoId = 0 export const addTodo = text => ({ type: 'ADD_TODO', id: nextTodoId++, text })export const setVisibilityFilter = filter => ({ type: 'SET_VISIBILITY_FILTER', filter })export const toggleTodo = id => ({ type: 'TOGGLE_TODO', id })export const VisibilityFilters = { SHOW_ALL: 'SHOW_ALL', SHOW_COMPLETED: 'SHOW_COMPLETED', SHOW_ACTIVE: 'SHOW_ACTIVE' }

Index.spec.js
import * as actions from './index'describe('todo actions', () => { it('addTodo should create ADD_TODO action', () => { expect(actions.addTodo('Use Redux')).toEqual({ type: 'ADD_TODO', id: 0, text: 'Use Redux' }) })it('setVisibilityFilter should create SET_VISIBILITY_FILTER action', () => { expect(actions.setVisibilityFilter('active')).toEqual({ type: 'SET_VISIBILITY_FILTER', filter: 'active' }) })it('toggleTodo should create TOGGLE_TODO action', () => { expect(actions.toggleTodo(1)).toEqual({ type: 'TOGGLE_TODO', id: 1 }) }) })

步骤4: reducer我们知道,动作只会触发应用程序中的更改,而还原器会指定这些更改。reducer是一个函数,它接受两个参数“Action”和“State”来计算和返回一个更新后的状态。它从“操作”读取有效载荷,然后通过相应的状态更新“存储”。
在给定的文件中,每个减速器管理自己的全局状态部分。状态参数对于每个reducer都是不同的,并且对应于它管理的“状态”部分。当应用程序变大时,我们可以将还原器分割成单独的文件,使它们完全独立,并管理不同的数据域。
在这里,我们使用“combineReducers”的辅助功能来添加我们将来可能使用的任何新的reducer。
index.js
import { combineReducers } from 'redux' import todos from './todos' import visibilityFilter from './visibilityFilter'export default combineReducers({ todos, visibilityFilter })

Todos.js
const todos = (state = [], action) => { switch (action.type) { case 'ADD_TODO': return [ ...state, { id: action.id, text: action.text, completed: false } ] case 'TOGGLE_TODO': return state.map(todo => (todo.id === action.id) ? {...todo, completed: !todo.completed} : todo ) default: return state } } export default todos

Todos.spec.js
import todos from './todos'describe('todos reducer', () => { it('should handle initial state', () => { expect( todos(undefined, {}) ).toEqual([]) })it('should handle ADD_TODO', () => { expect( todos([], { type: 'ADD_TODO', text: 'Run the tests', id: 0 }) ).toEqual([ { text: 'Run the tests', completed: false, id: 0 } ])expect( todos([ { text: 'Run the tests', completed: false, id: 0 } ], { type: 'ADD_TODO', text: 'Use Redux', id: 1 }) ).toEqual([ { text: 'Run the tests', completed: false, id: 0 }, { text: 'Use Redux', completed: false, id: 1 } ])expect( todos([ { text: 'Run the tests', completed: false, id: 0 }, { text: 'Use Redux', completed: false, id: 1 } ], { type: 'ADD_TODO', text: 'Fix the tests', id: 2 }) ).toEqual([ { text: 'Run the tests', completed: false, id: 0 }, { text: 'Use Redux', completed: false, id: 1 }, { text: 'Fix the tests', completed: false, id: 2 } ]) })it('should handle TOGGLE_TODO', () => { expect( todos([ { text: 'Run the tests', completed: false, id: 1 }, { text: 'Use Redux', completed: false, id: 0 } ], { type: 'TOGGLE_TODO', id: 1 }) ).toEqual([ { text: 'Run the tests', completed: true, id: 1 }, { text: 'Use Redux', completed: false, id: 0 } ]) }) })

VisibilityFilter.js
import { VisibilityFilters } from '../actions'const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => { switch (action.type) { case 'SET_VISIBILITY_FILTER': return action.filter default: return state } } export default visibilityFilter

步骤5: 组件它是一个表示组件,它关心事物的外观,如标记、样式。它接收数据并仅通过props调用回调。它不知道数据来自何处,也不知道如何更改数据。它只呈现给他们的东西。
App.js
它是在UI中呈现所有内容的根组件。
import React from 'react' import Footer from './Footer' import AddTodo from '../containers/AddTodo' import VisibleTodoList from '../containers/VisibleTodoList'const App = () => ( < div> < AddTodo /> < VisibleTodoList /> < Footer /> < /div> ) export default App

Footer.js
它告诉用户在哪里更改当前可见的todos。
import React from 'react' import FilterLink from '../containers/FilterLink' import { VisibilityFilters } from '../actions'const Footer = () => ( < p> Show: < FilterLink filter={VisibilityFilters.SHOW_ALL}>All< /FilterLink> {', '} < FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>Active< /FilterLink> {', '} < FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>Completed< /FilterLink> < /p> ) export default Footer

Link.js
它是一个带有回调的链接。
import React from 'react' import PropTypes from 'prop-types'const Link = ({ active, children, onClick }) => { if (active) { return < span>{children}< /span> }return ( < a href="" onClick={e => { e.preventDefault() onClick() }} > {children} < /a> ) }Link.propTypes = { active: PropTypes.bool.isRequired, children: PropTypes.node.isRequired, onClick: PropTypes.func.isRequired }export default Link

Todo.js
它表示一个显示文本的todo项。
import React from 'react' import PropTypes from 'prop-types'const Todo = ({ onClick, completed, text }) => ( < li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none' }} > {text} < /li> )Todo.propTypes = { onClick: PropTypes.func.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired }export default Todo

TodoList.js
它是一个列表显示可见的todos{id, text, completed}。
import React from 'react' import PropTypes from 'prop-types' import Todo from './Todo'const TodoList = ({ todos, onTodoClick }) => ( < ul> {todos.map((todo, index) => ( < Todo key={index} {...todo} onClick={() => onTodoClick(index)} /> ))} < /ul> )TodoList.propTypes = { todos: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.number.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired }).isRequired ).isRequired, onTodoClick: PropTypes.func.isRequired } export default TodoList

步骤6:容器它是一个容器组件,负责处理数据获取、更新状态等工作。它为表示组件或其他容器组件提供数据和行为,它使用Redux状态来读取数据并分派Redux动作来更新数据。
AddTodo.js
它包含带有ADD (submit)按钮的输入字段。
import React from 'react' import { connect } from 'react-redux' import { addTodo } from '../actions'const AddTodo = ({ dispatch }) => { let inputreturn ( < div> < form onSubmit={e => { e.preventDefault() if (!input.value.trim()) { return } dispatch(addTodo(input.value)) input.valuehttp://www.srcmini.com/= '' }}> < input ref={node => input = node} /> < button type="submit"> Add Todo < /button> < /form> < /div> ) } export default connect()(AddTodo)

FilterLink.js
它表示当前的可见的filter并呈现一个链接。
import { connect } from 'react-redux' import { setVisibilityFilter } from '../actions' import Link from '../components/Link'const mapStateToProps = (state, ownProps) => ({ active: ownProps.filter === state.visibilityFilter })const mapDispatchToProps = (dispatch, ownProps) => ({ onClick: () => dispatch(setVisibilityFilter(ownProps.filter)) })export default connect( mapStateToProps, mapDispatchToProps )(Link)

VisibleTodoList.js
它过滤todos并呈现TodoList。
import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' import { VisibilityFilters } from '../actions'const getVisibleTodos = (todos, filter) => { switch (filter) { case VisibilityFilters.SHOW_ALL: return todos case VisibilityFilters.SHOW_COMPLETED: return todos.filter(t => t.completed) case VisibilityFilters.SHOW_ACTIVE: return todos.filter(t => !t.completed) default: throw new Error('Unknown filter: ' + filter) } }const mapStateToProps = state => ({ todos: getVisibleTodos(state.todos, state.visibilityFilter) })const mapDispatchToProps = dispatch => ({ toggleTodo: id => dispatch(toggleTodo(id)) })export default connect( mapStateToProps, mapDispatchToProps )(TodoList)

步骤7:储存所有容器组件都需要访问Redux存储才能订阅它。为此,我们需要将它(存储)作为一个props传递给每个容器组件,但是我们建议使用特殊的React Redux组件,它使存储对所有容器组件可用,而无需显式地传递它。它只在呈现根组件时使用一次。
【React实现Redux实例全解 – ReactJS实战教程】index.js
import React from 'react' import { render } from 'react-dom' import { createStore } from 'redux' import { Provider } from 'react-redux' import App from './components/App' import rootReducer from './reducers'const store = createStore(rootReducer)render( < Provider store={store}> < App /> < /Provider>, document.getElementById('root') )

    推荐阅读