Redux状态更改是什么
在涉及”Redux状态更改”这个主题之前,我们应该先了解什么是Redux。
"A Predictable State Container for JS Apps"
换句话说,Redux是一种基于模式的库,用于管理和更新应用程序状态。为了管理和更新应用程序状态,Redux使用称为“action”的事件。
Redux具有一些特性,使其受欢迎,其中一些特性如下所述:
- Redux具有可预测性属性,意味着它可以在不同的环境中运行,如客户端、服务器和本机。
- Redux将应用程序状态集中到一个地方,您可以轻松执行撤消、重做、状态持久性等操作。
- Redux还具有调试属性。换句话说,Redux具有称为Redux Devtools的工具,使应用程序易于调试,可以知道应用程序状态何时、在哪里、为什么以及如何发生变化。
- 此外,Redux具有灵活性属性,可以适用于任何类型的UI层。
让我们举一个示例,以便更容易理解Redux。假设我们有一个食品应用程序,我们可以使用Redux存储库来管理食谱。为了管理应用程序中的食谱,我们有一些基本方法可以帮助我们做到这一点,例如:
我们只有一个 存储库 来管理整个应用程序的状态。同时, 操作方法 用于进行必要的更改。 Reducer 就像是安全保卫者,根据请求的操作方式知道如何改变状态。最后, 中间件 用于处理应用程序中执行的任务。
随着时间的推移,应用程序的规模不断增长。此时,管理状态和调试状态对于应用程序来说是一个非常庞大的任务。管理状态并找到应用程序的状态何时何地发生变化也是一项挑战。有时候,当客户端调用某些API或对一些数据进行更改时,会在应用程序的状态上进行一些更新。因此,我们需要一个模型或管理系统来跟踪应用程序的状态。为此,使用了Redux,因为Redux是可预测的状态容器。为提供此服务,Redux使用了许多组件,如下所述:
- 存储(Store)
- Reducer
- Action
1. Redux存储(Store): 在Redux中,Redux存储是Redux的主要组件(第一个基本原则)。因为所有的事物都通过Redux存储在应用程序中。Redux存储是一个单一存储,用于存储应用程序的所有状态和数据。Redux存储中发生的任何更改都会反映在整个应用程序中。因此,为了对状态进行必要的更改,我们执行以下步骤:
步骤1: 存储应用程序状态
存储的工作流程是:
- 首先,Redux存储会获取应用程序状态的信息。
- 然后,它会存储发生在状态上的更改。
- 此后,存储会对应用程序的状态进行必要的更改。
Redux状态中描述的帮助我们进行状态管理的函数如下所示:
- getState()是用于获取有关应用程序当前状态的函数。
- 更新状态时,Redux使用dispatch(action)。这是用于在应用程序中进行更新的一个函数。
- 然后,存储库侦听状态中的更改。为此,Redux使用subscribe(listener)来查看更改并更新状态。
现在我们能够存储应用程序的当前状态。接下来,我们需要更新状态,以处理应用程序状态中的下一次更改。
步骤2: 更新应用程序的状态。为了进行必要的更改,或者可以说是为了进行应用程序状态的更新,我们使用操作。操作是Redux的主要组件,因为它具有一种特殊的属性,即类型属性,帮助Redux了解即将发生的状态更改。因此,我们可以说操作具有描述性质 类型 。
例如,我们要设计一个书店应用程序。然后用户想要向商店添加一本书。为此,我们使用操作函数来更改存储。操作调用类型属性如下所示:
{ type: "ADD_BOOK" }
为此,我们使用store.dispatch()函数。该函数被库用于接受在状态中执行的操作。这是Redux基础原理的第二条。
现在,通过所有这些,我们能够对应用程序的状态进行更新。现在我们使用这些操作来进行实际的更新。
步骤3: 通过reducers进行更改。为了在应用程序的状态中进行更改,我们使用纯函数。这些函数将当前状态作为参数并使用操作进行更改。然后返回更新后的应用程序状态。这些函数称为Reducers。这些函数在更改新的对象时不会操纵应用程序的状态,从而能够对应用程序状态进行管理或跟踪更改。Reducers的主要工作是收集一些值,将它们减少到更新后的状态,然后返回它。
因此,最终我们能够对应用程序进行更改。并且借助Store、Reducers和Actions跟踪每个状态。
让我们通过示例讨论Redux状态的变化:
示例: 在本节中,我们将讨论Redux状态变化的示例。以下是所需的步骤:在这里,我们将设计一个用于执行待办任务的react-redux应用程序。因此,我们需要执行以下步骤:
步骤1: 为了创建新的react应用程序,我们需要在终端中执行以下代码:
npx create-react-app todo
步骤2: 现在,在终端中创建并执行上述代码后,我们将得到一个名为 todo 的文件夹。 然后再次进入终端,键入:
cd todo
通过这个,我们可以说终端现在指向todo文件夹中的文件。
步骤3: 这里我们将创建一个package.json文件,主要用于定义项目中要使用的依赖项。这就是我们创建这个文件的原因。
package.json:
{
"name": "todos",
"version": "0.0.1",
"private": true,
"devDependencies": {
"react-scripts": "^4.0.3"
},
"dependencies": {
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.0",
"redux": "^4.0.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"eject": "react-scripts eject",
"test": "react-scripts test --env=node"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
在定义依赖关系之后,我们必须通过在终端中键入以下代码来安装它们:
注意: 建议在项目中使用终端,用于启动服务器或安装所需的依赖项。
npm install
步骤4: 创建必要的文件。在上述步骤完成之后,我们的todo文件夹中有以下文件:
- node_modules
- public
- src
- package-lock.json
- package.json
现在,删除public和src文件中的所有文件。所以,在执行这些操作之后,我们将创建以下文件:
在public文件夹中创建文件 index.html. index.html文件基本上用于获取应用程序所需的所有数据。它是应用程序的主页。
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content=
"width=device-width, initial-scale=1">
<title>Redux Todo List</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
现在,我们要创建以下文件夹和文件:
- actions
- components
- containers
- reducers
- index.js
index.js文件: 它是用来提取所有文件的数据的文件,比如存储、减速器、行动等。或者我们可以说它是react-redux应用程序的存储。所有容器都需要访问它以获取进一步处理的数据,如更新、更改等等。
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')
)
在 actions 文件夹中,我们使用称为“ type ”的属性,用于通知发送到存储的数据。我们将创建以下文件:
- index.js
- index.spec.js
index.js文件: 在这个文件中,创建了一个要创建的操作,它还提供了它的id(唯一的数字或标识符)。同时也定义了当前操作的可见性过滤器active。
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
})
})
})
步骤5: 在此之后,我们将在 components 文件夹中创建以下文件:
- App.js
- Footer.js
- Link.js
- Todo.js
- TodoList.js
组件文件夹是React-Redux应用程序的展示部分,意味着我们在该文件夹中定义与应用程序的外观相关的文件,如样式、布局等等。它仅用于接收数据并通过props渲染数据。
App.js文件: 它是主要的或者可以说是根组件,需要在应用程序的用户界面上渲染。
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 文件: 在这个文件(或者说是在用户创建的应用程序中)决定哪些更改应该在哪里可见。
import React from 'react'
import FilterLink from '../containers/FilterLink'
import { VisibilityFilters } from '../actions'
const Footer = () => (
<div>
<span>Show: </span>
<FilterLink filter={VisibilityFilters.SHOW_ALL}>
All
</FilterLink>
<FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>
Active
</FilterLink>
<FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>
Completed
</FilterLink>
</div>
)
export default Footer
Link.js文件: 用于使用决定当前状态的回调函数,例如active,completed等的动作。
import React from 'react'
import PropTypes from 'prop-types'
const Link = ({ active, children, onClick }) => (
<button
onClick={onClick}
disabled={active}
style={{
marginLeft: '4px',
}}
>
{children}
</button>
)
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文件:
用于显示当前状态下可用的待办事项,格式为{id,text,completed}
import React from 'react'
import PropTypes from 'prop-types'
import Todo from './Todo'
const TodoList = ({ todos, toggleTodo }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => toggleTodo(todo.id)}
/>
)}
</ul>
)
TodoList.propTypes = {
todos: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}).isRequired).isRequired,
toggleTodo: PropTypes.func.isRequired
}
export default TodoList
现在,我们将在 containers 文件夹中创建以下文件:
- AddTodo.js
- FilterLink.js
- VisibleTodoList.js
在容器组件中,正如我们之前学到的,它始终关注于工作的事物,即数据如何被获取到其他文件中或者更新如何发生。它为组件提供数据,或者可以说为在UI中呈现的组件提供布局数据。它还使用redux状态功能来读取和分发redux操作,以更新当前UI组件中的数据。
AddTodo.js文件: 它包含用户希望通过“添加”(提交)按钮添加到待办事项列表中的待办事项文本。
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
const AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}>
<input ref={node => input = node} />
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
export default connect()(AddTodo)
FilerLink.js 文件: 用于设置需要设置在待办事项当前状态上的可见性过滤器。
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文件: 它过滤并在应用程序页面上呈现项目。
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)
步骤6: 现在,我们将在 reducers 文件夹中创建以下文件:
- index.js
- todos.js
- todos.spec.js
- visibilityFilter.js
正如我们之前学到的,动作仅用于对应用程序进行更改,而reducer用于在应用程序中呈现这些更改。因此,reducer是一个接受两个参数“Action”和“State”的函数。它读取动作发送的数据,并通过redux功能在“store”中更新它。
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
运行应用程序的步骤: 在执行上述步骤后,我们完成了我们的Redux管理应用程序。要启动该应用程序,我们应该在终端中输入。
npm start
输出: