NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

Redux+Hook 重写 Todo List,用代码实例挽回摒弃 Redux 的用户

  • 2019-06-24
  • 本文字数:3985 字

    阅读完需:约 13 分钟

Redux+Hook重写Todo List,用代码实例挽回摒弃Redux的用户

作者开发了一个名为“reactive-react-redux”的库,尽管它基于 Redux,但和传统方法又有一些区别。作者基于这个库给出了 Redux 中 Todo List 的示例代码。


如果你已经在用 React Redux 并爱上它,可能会不理解为什么人们尝试使用 React 中的 context 和 hook 来替换 Redux,即所谓“去 Redux 化”。


有些人认为 Redux DevTools 的扩展工具和中间件蛮不错的,对于他们来说,Redux 和 context + hook 实际上是两种选项。Context + hook 可以在组件之间实现状态共享,但是随着 APP 变得越来越大,有可能还是需要引入 Redux 或其他类似的解决方案,否则,最终运行中会出现太多上下文而无法进行顺畅处理。但是,我得承认这只是假设,将来或许能够找到更好的解决方案。


我最近一直在开发一个名为“reactive-react-redux”的库,尽管它基于 Redux,但和传统方法又有一些区别。Github 地址:https://github.com/dai-shi/reactive-react-redux


它的 API 非常简单直观,而且 Proxy 让它的性能得到了优化。我希望这个库能挽回一些用 context + hook 去替代 Redux 的开发人员,为此我还基于这个库写了代码示例。下面这个示例实现的是 Redux 中著名的 Todo List。


这个示例是用 TypeScript 语言写的。如果你不熟悉 TypeScript,请尝试忽略 State、Action 和*Type 这些关键字。

类型定义和状态还原器(reducer)

State 和 Action 的类型定义定义如下:


./src/types/index.ts


export type VisibilityFilterType =  | 'SHOW_ALL'  | 'SHOW_COMPLETED'  | 'SHOW_ACTIVE';
export type TodoType = { id: number; text: string; completed: boolean;};
export type State = { todos: TodoType[]; visibilityFilter: VisibilityFilterType;};
export type Action = | { type: 'ADD_TODO'; id: number; text: string } | { type: 'SET_VISIBILITY_FILTER'; filter: VisibilityFilterType } | { type: 'TOGGLE_TODO'; id: number };
复制代码


状态还原器(reducer)的代码几乎与原始示例一样,如下所示。


./src/reducers/index.ts


import { combineReducers } from 'redux';
import todos from './todos';import visibilityFilter from './visibilityFilter';
export default combineReducers({ todos, visibilityFilter,});
复制代码


./src/reducers/todos.ts


import { TodoType, Action } from '../types';
const todos = (state: TodoType[] = [], action: Action): TodoType[] => { switch (action.type) { case 'ADD_TODO': return [ ...state, { id: action.id, text: action.text, completed: false, }, ]; case 'TOGGLE_TODO': return state.map((todo: TodoType) => ( todo.id === action.id ? { ...todo, completed: !todo.completed } : todo )); default: return state; }};
export default todos;
复制代码


./src/reducers/visibilityFilter.ts


import { Action, VisibilityFilterType } from '../types';
const visibilityFilter = ( state: VisibilityFilterType = 'SHOW_ALL', action: Action,): VisibilityFilterType => { switch (action.type) { case 'SET_VISIBILITY_FILTER': return action.filter; default: return state; }};
export default visibilityFilter;
复制代码

动作生成器(Action creators)

有几种方法都可以用来实现动作分派。而我的选择是为每个动作创建 hook。注意,这方面我们仍在探索更好的实现方式。


./src/actions/index.ts


import { useCallback } from 'react';import { useReduxDispatch } from 'reactive-react-redux';
import { Action, VisibilityFilterType } from '../types';
let nextTodoId = 0;
export const useAddTodo = () => { const dispatch = useReduxDispatch<Action>(); return useCallback((text: string) => { dispatch({ type: 'ADD_TODO', id: nextTodoId++, text, }); }, [dispatch]);};
export const useSetVisibilityFilter = () => { const dispatch = useReduxDispatch<Action>(); return useCallback((filter: VisibilityFilterType) => { dispatch({ type: 'SET_VISIBILITY_FILTER', filter, }); }, [dispatch]);};
export const useToggleTodo = () => { const dispatch = useReduxDispatch<Action>(); return useCallback((id: number) => { dispatch({ type: 'TOGGLE_TODO', id, }); }, [dispatch]);};
复制代码


以上实现其实并非真正意义上的动作生成器,而是返回动作分派器的 hook。

组件

我们并不在这里区分演示组件(presentational components)和容器组件(container components)。当然如何构造组件仍然是个值得探讨的话题,但是在本例中,组件都被视为扁平的。


./src/components/App.tsx:App 也和原始示例保持一致。


import * as React from 'react';
import Footer from './Footer';import AddTodo from './AddTodo';import VisibleTodoList from './VisibleTodoList';
const App: React.FC = () => ( <div> <AddTodo /> <VisibleTodoList /> <Footer /> </div>);
export default App;
复制代码


./src/components/Todo.tsx:这里做了一些小的修改,但没有特别大的改动。


import * as React from 'react';
type Props = { onClick: (e: React.MouseEvent) => void; completed: boolean; text: string;};
const Todo: React.FC<Props> = ({ onClick, completed, text }) => ( <li onClick={onClick} role="presentation" style={{ textDecoration: completed ? 'line-through' : 'none', cursor: 'pointer', }} > {text} </li>);
export default Todo;
复制代码


./src/components/VisibleTodoList.tsx:这里并未出现 mapStateToProps 或 selector 函数,只是在 render 中调用 getVisibleTodos。


import * as React from 'react';import { useReduxState } from 'reactive-react-redux';
import { TodoType, State, VisibilityFilterType } from '../types';import { useToggleTodo } from '../actions';import Todo from './Todo';
const getVisibleTodos = (todos: TodoType[], filter: VisibilityFilterType) => { switch (filter) { case 'SHOW_ALL': return todos; case 'SHOW_COMPLETED': return todos.filter(t => t.completed); case 'SHOW_ACTIVE': return todos.filter(t => !t.completed); default: throw new Error(`Unknown filter: ${filter}`); }};
const VisibleTodoList: React.FC = () => { const state = useReduxState<State>(); const visibleTodos = getVisibleTodos(state.todos, state.visibilityFilter); const toggleTodo = useToggleTodo(); return ( <ul> {visibleTodos.map(todo => ( <Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} /> ))} </ul> );};
export default VisibleTodoList;
复制代码


./src/components/FilterLink.tsx:同样,当 useReduxState 函数返回整个 Redux 状态对象时,程序只是使用其属性对 active 进行评估。


import * as React from 'react';import { useReduxState } from 'reactive-react-redux';
import { useSetVisibilityFilter } from '../actions';import { State, VisibilityFilterType } from '../types';
type Props = { filter: VisibilityFilterType;};
const FilterLink: React.FC<Props> = ({ filter, children }) => { const state = useReduxState<State>(); const active = filter === state.visibilityFilter; const setVisibilityFilter = useSetVisibilityFilter(); return ( <button type="button" onClick={() => setVisibilityFilter(filter)} disabled={active} style={{ marginLeft: '4px', }} > {children} </button> );};
export default FilterLink;
复制代码


./src/components/Footer.tsx:由于有类型检查的保证,可以将字符串传递给 FilterLink 组件的 filter 属性。


import * as React from 'react';
import FilterLink from './FilterLink';
const Footer: React.FC = () => ( <div> <span>Show: </span> <FilterLink filter="SHOW_ALL">All</FilterLink> <FilterLink filter="SHOW_ACTIVE">Active</FilterLink> <FilterLink filter="SHOW_COMPLETED">Completed</FilterLink> </div>);
export default Footer;
复制代码


./src/components/AddTodo.tsx:这里对原始示例进行了一些修改,以便使用带有 useState 的受控表单。


import * as React from 'react';import { useState } from 'react';
import { useAddTodo } from '../actions';
const AddTodo = () => { const [text, setText] = useState(''); const addTodo = useAddTodo(); return ( <div> <form onSubmit={(e) => { e.preventDefault(); if (!text.trim()) { return; } addTodo(text); setText(''); }} > <input value={text} onChange={e => setText(e.target.value)} /> <button type="submit">Add Todo</button> </form> </div> );};
export default AddTodo;
复制代码

在线演示

请打开你的浏览器访问codesandbox,运行该示例。你也可以在GitHub上找到所有源代码。

其他信息

这篇文章中,并没有解释关于 reactive- response -redux 的内部细节。请访问GitHub查看更多信息。


英文原文:https://blog.axlight.com/posts/redux-meets-hooks-for-non-redux-users-a-small-concrete-example-with-reactive-react-redux/



2019-06-24 19:335500
用户头像

发布了 63 篇内容, 共 41.2 次阅读, 收获喜欢 119 次。

关注

评论

发布
暂无评论
发现更多内容

helm 3.8 命令指南

ghostwritten

Kubernetes Helm

汽车行业云,如何管“多云”?

BoCloud博云

案例 汽车 云管理平台

2. helm 简介

ghostwritten

Kubernetes Helm

AIRIOT物联网低代码平台如何配置db-driver驱动?

AIRIOT

低代码开发 驱动配置

贡献者任务第三期精彩来袭!

第四范式开发者社区

人工智能 机器学习 数据库 开源 贡献者

【Python】题集 of ③

謓泽

5月月更

5. helm charts 基础入门

ghostwritten

Kubernetes Helm

helm 控制流程:循环、判断

ghostwritten

Kubernetes Helm

TiDB【城市开拓招募帖】回答“社区活动什么时候来我的城市?”

TiDB 社区干货传送门

Java基础概念干货(字符、标识符

爱好编程进阶

Java 程序员 后端开发

大数据培训Flink整合ElasticSearch

@零度

大数据 flink elasticsearch

TiDB 6.0 Book Rush!一起来分布式创作 6.0 的使用手册吧!

TiDB 社区干货传送门

helm 注意事项

ghostwritten

Kubernetes Helm

转行运维工程师之后,我先把这几个Linux 命令记在了本子上,实干9场景

梦想橡皮擦

5月月更

Java多线程基础(二)

爱好编程进阶

程序员 后端开发

Java多线程知识点总结(思维导图+源码笔记

爱好编程进阶

Java 程序员 后端开发

1. helm 目录

ghostwritten

Helm #Kubernetes#

helm NOTES.txt

ghostwritten

Kubernetes Helm

web技术支持| 从视频元素流式传输到视频元素

anyRTC开发者

前端 Web 音视频 WebRTC 视频通话

helm charts 内置函数

ghostwritten

Kubernetes Helm

java培训redis集群原理分享

@零度

redis JAVA开发

helm 自动滚动部署

ghostwritten

Kubernetes Helm

巴延兴:从主导多个SIG组到OpenHarmony “代码贡献之星”,我是如何做到的?

OpenHarmony开发者

OpenHarmony sig

helm 命名模板

ghostwritten

Kubernetes Helm

helm v2 公共源大全

ghostwritten

Kubernetes Helm

用一个性能提升了666倍的小案例说明在TiDB中正确使用索引的重要性

TiDB 社区干货传送门

对代码不感冒, 我应该怎么办?

源字节1号

程序员人生 后端开发

4. helm 3.8.0 入门

ghostwritten

Kubernetes Helm

helm v2 hooks

ghostwritten

Kubernetes Helm

helm values.yaml

ghostwritten

Kubernetes Helm

helm 模板函数与管道

ghostwritten

Kubernetes Helm

Redux+Hook重写Todo List,用代码实例挽回摒弃Redux的用户_大前端_Daishi Kato_InfoQ精选文章