智能体刷屏的背后,是 AI 应用拐点的来临?AICon 北京站议程重磅公布,50+ 硬核分享不容错过 了解详情
写点什么

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:335661
用户头像

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

关注

评论

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

AIrserver2022手机软件无线投屏电脑屏幕

茶色酒

AirServer

FinClip小程序+Rust(三):一个加密钱包

Speedoooo

rust 前端框架 小程序容器

Qt软件开发_解决中文路径无法识别问题

DS小龙哥

5月月更

AIRIOT物联网低代码平台如何配置三菱PLC驱动?

AIRIOT

物联网 PLC 驱动配置

有数BI大规模报告稳定性保障实践

网易数帆

大数据 BI 网易 稳定性保障

FFmpeg介绍

Loken

音视频 5月月更

学Python运维,这知识点你肯定会遇到,【必收藏之】nginx 域名跳转相关配置

梦想橡皮擦

5月月更

RocketMQ—Producer(五)路由队列选择

IT巅峰技术

架构 RocketMQ java

战“码”先锋直播预告丨如何成为一名优秀的OpenHamrony贡献者?

OpenHarmony开发者

OpenHarmony 贡献代码

javascript 中搜索数组的四种方法

CRMEB

FinClip小程序+Rust(四):端到端融合

Speedoooo

rust 前端框架 小程序容器

前端常用的站点导航

刘帅强

马斯克推崇的柏拉图式元宇宙,PlatoFarm早已验证出答案

股市老人

Dockerfile 的最佳实践 | Dockerfile 你写的都对么?

xcbeyond

最佳实践 镜像 Dockerfile

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

汪子熙

CRM SAP 客户关系管理系统 5月月更 订单模型

百问百答第38期:关于nginx监控,我们这样做

博睿数据

博睿数据 nginx监控

FinClip小程序+Rust(五):用内联SVG实现二维码

Speedoooo

rust 前端框架 小程序容器

Druid 连接池源码阅读 06

石小天

网站开发进阶(六十八)CSS3媒体查询@media

No Silver Bullet

5月月更 媒体查询 设备自适应 hsl

稳扎稳打步步为营 英特尔GPU将在消费市场和数据中心全面开花

科技新消息

总结

Geek_36cc7c

druid源码学习六

Nick

jdk JMX Druid

Magento 和 WordPress 的区别

海拥(haiyong.site)

WordPress 5月月更

层层剖析一次 HTTP POST 请求事故

vivo互联网技术

HTTP CORS XSS 跨域 waf

OpenHarmony 3.1 Release版本关键特性解析——Enhanced SWAP内存管理

OpenHarmony开发者

内存管理 OpenHarmony

FinClip小程序里如何安全使用SVG

Speedoooo

rust SVG 前端框架 小程序容器

Kubernetes下web服务的性能测试三部曲之三:横向扩容

程序员欣宸

Java Kubernetes 5月月更

CUDA优化之PReLU性能调优

OneFlow

性能优化 cuda PReLU 朴素实现

熊磊:成功移植OpenHarmony到多套开发板,是最开心的事

OpenHarmony开发者

开发板 OpenHarmony

推荐几个机器学习的好资源

AIWeker

人工智能 深度学习 5月月更

Linux下玩转nginx系列(五)---nginx实现负载均衡

anyRTC开发者

nginx Linux 负载均衡 音视频

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