写点什么

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

2019 年 6 月 24 日

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 年 6 月 24 日 19:334947
用户头像

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

关注

评论

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

“五年饮冰,难凉热血”,一名专科生的求学历程

不脱发的程序猿

程序人生 心路历程 2月春节不断更 大学总结 2020年度总结

大作业1-同城快递业务系统设计

arcyao

期末大作业一

心在那片海

即使技术再精,面试时一问这个必挂!!

冰河

面试 类加载器 我要进大厂 Java类加载

智慧社区服务平台,平安社区搭建

135深圳3055源中瑞8032

架构师训练营结课作业

Rocky·Chen

史上最全的技术手册整理总结,编程小白都从这篇文章迅速成为大牛

孙叫兽

Java 前端 技术手册 开发文档

2020年末总结,脚踏实地,一步一个脚印——致敬自己一年的心酸历程

孙叫兽

孙叫兽 年度报告

使用APICloud敏捷式开发总结,回顾开发一个完整APP过程。

孙叫兽

App 开发 APICloud

如何制作和使用自签名证书

soulteary

Docker SSL证书

《零基础看得懂的Python入门教程 》——(一)既然你选择了这系列教程那么我就要让你听得懂

1_bit

Python 编程语言 学习经验

驱动力读书笔记之四

张老蔫

28天写作

百度网盘限速解决方案

孙叫兽

解决方案 百度网盘 限速

阿里架构师经验分享!写给互联网大厂员工的真心话,最全的BAT大厂面试题整理

欢喜学安卓

android 程序员 面试 移动开发

从零开始学Android!15个经典面试问题及回答思路,这原因我服了

欢喜学安卓

android 程序员 面试 移动开发

重磅发布 | 2021年OpenAtom XuperChain开源技术路径

开放原子开源基金会

区块链 百度 开源 开放原子开源基金会

智慧组工系统开发解决方案,组织部干部管理平台搭建

WX13823153201

智慧组工系统开发

广西党建智慧平台方案,智慧组工信息化建设

135深圳3055源中瑞8032

OpenCV简介及其工程应用-游戏色块检测

行者AI

OpenCV

Elasticsearch multi-index 搜索

escray

elastic 七日更 死磕Elasticsearch 60天通过Elastic认证考试 2月春节不断更

程序员养家活口接私活必备网站(顺便用技术改变世界)

孙叫兽

程序员 网站 私活

让人“眼前一亮、不明觉厉”的互联网技术PPT

不脱发的程序猿

程序人生 PPT 2月春节不断更 互联网技术PPT 互联网工具

股票配资系统开发

v16629866266

产品 0 期 - 第四周作业

vipyinzhiwei

话题讨论 |互联网软件技术培训,靠谱吗?

不脱发的程序猿

程序员 程序人生 话题讨论 互联网培训 技术培训

期末大作业二

心在那片海

婚恋交友软件开发

luluhulian

大作业2-知识总结

arcyao

复盘银行的区块链实践:从分布式账本,到产业数字化

CECBC区块链专委会

银行 银行大数据

Python实现钉钉/企业微信自动打卡

sum56

Python python 爬虫 打卡

区块链珠宝溯源平台,区块链溯源解决方案

135深圳3055源中瑞8032

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