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

栏目: IOS · Android · 发布时间: 5年前

内容简介:作者开发了一个名为“reactive-react-redux”的库,尽管它基于 Redux,但和传统方法又有一些区别。作者基于这个库给出了 Redux 中 Todo List 的示例代码。如果你已经在用 React Redux 并爱上它,可能会不理解为什么人们尝试使用 React 中的 context 和 hook 来替换 Redux,即所谓“去 Redux 化”。有些人认为 Redux DevTools 的扩展工具和中间件蛮不错的,对于他们来说,Redux 和 context + hook 实际上是两种选

作者开发了一个名为“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

复制代码

exporttypeVisibilityFilterType =
|'SHOW_ALL'
|'SHOW_COMPLETED'
|'SHOW_ACTIVE';

exporttypeTodoType = {
id:number;
text:string;
completed:boolean;
};

exporttypeState = {
todos: TodoType[];
visibilityFilter: VisibilityFilterType;
};

exporttypeAction =
| {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';

importtodosfrom'./todos';
importvisibilityFilterfrom'./visibilityFilter';

exportdefaultcombineReducers({
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':
returnstate.map((todo: TodoType) => (
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
));
default:
returnstate;
}
};

exportdefaulttodos;

./src/reducers/visibilityFilter.ts

复制代码

import{ Action, VisibilityFilterType }from'../types';

constvisibilityFilter = (
state: VisibilityFilterType ='SHOW_ALL',
action: Action,
):VisibilityFilterType=>{
switch(action.type) {
case'SET_VISIBILITY_FILTER':
returnaction.filter;
default:
returnstate;
}
};

exportdefaultvisibilityFilter;

动作生成器(Action creators)

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

./src/actions/index.ts

复制代码

import{ useCallback }from'react';
import{ useReduxDispatch }from'reactive-react-redux';

import{ Action, VisibilityFilterType }from'../types';

letnextTodoId =0;

exportconstuseAddTodo =()=>{
constdispatch = useReduxDispatch<Action>();
returnuseCallback((text:string) =>{
dispatch({
type:'ADD_TODO',
id: nextTodoId++,
text,
});
}, [dispatch]);
};

exportconstuseSetVisibilityFilter =()=>{
constdispatch = useReduxDispatch<Action>();
returnuseCallback((filter: VisibilityFilterType) =>{
dispatch({
type:'SET_VISIBILITY_FILTER',
filter,
});
}, [dispatch]);
};

exportconstuseToggleTodo =()=>{
constdispatch = useReduxDispatch<Action>();
returnuseCallback((id:number) =>{
dispatch({
type:'TOGGLE_TODO',
id,
});
}, [dispatch]);
};

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

组件

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

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

复制代码

import*asReactfrom'react';

importFooterfrom'./Footer';
importAddTodofrom'./AddTodo';
importVisibleTodoListfrom'./VisibleTodoList';

constApp: React.FC =()=>(
<div>
<AddTodo/>
<VisibleTodoList/>
<Footer/>
</div>
);

exportdefaultApp;

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

复制代码

import*asReactfrom'react';

type Props = {
onClick:(e: React.MouseEvent) =>void;
completed: boolean;
text: string;
};

constTodo: React.FC<Props> =({ onClick, completed, text }) =>(
<li
onClick={onClick}
role="presentation"
style={{
textDecoration:completed? 'line-through':'none',
cursor:'pointer',
}}
>
{text}
</li>
);

exportdefaultTodo;

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

复制代码

import*asReactfrom'react';
import{ useReduxState }from'reactive-react-redux';

import{ TodoType, State, VisibilityFilterType }from'../types';
import{ useToggleTodo }from'../actions';
importTodofrom'./Todo';

constgetVisibleTodos =(todos: TodoType[], filter: VisibilityFilterType) =>{
switch(filter) {
case'SHOW_ALL':
returntodos;
case'SHOW_COMPLETED':
returntodos.filter(t=>t.completed);
case'SHOW_ACTIVE':
returntodos.filter(t=>!t.completed);
default:
thrownewError(`Unknown filter:${filter}`);
}
};

constVisibleTodoList: React.FC =()=>{
conststate = useReduxState<State>();
constvisibleTodos = getVisibleTodos(state.todos, state.visibilityFilter);
consttoggleTodo = useToggleTodo();
return(
<ul>
{visibleTodos.map(todo => (
<Todokey={todo.id}{...todo}onClick={()=>toggleTodo(todo.id)} />
))}
</ul>
);
};

export default VisibleTodoList;

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

复制代码

import*asReactfrom'react';
import{ useReduxState }from'reactive-react-redux';

import{ useSetVisibilityFilter }from'../actions';
import{ State, VisibilityFilterType }from'../types';

type Props = {
filter: VisibilityFilterType;
};

constFilterLink: React.FC<Props> =({ filter, children }) =>{
conststate = useReduxState<State>();
constactive = filter === state.visibilityFilter;
constsetVisibilityFilter = useSetVisibilityFilter();
return(
<button
type="button"
onClick={()=> setVisibilityFilter(filter)}
disabled={active}
style={{
marginLeft: '4px',
}}
>
{children}
</button>
);
};

exportdefaultFilterLink;

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

复制代码

import*asReactfrom'react';

importFilterLinkfrom'./FilterLink';

constFooter: React.FC =()=>(
<div>
<span>Show:</span>
<FilterLinkfilter="SHOW_ALL">All</FilterLink>
<FilterLinkfilter="SHOW_ACTIVE">Active</FilterLink>
<FilterLinkfilter="SHOW_COMPLETED">Completed</FilterLink>
</div>
);

exportdefaultFooter;

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

复制代码

import*asReactfrom'react';
import{ useState }from'react';

import{ useAddTodo }from'../actions';

constAddTodo =()=>{
const[text, setText] = useState('');
constaddTodo = useAddTodo();
return(
<div>
<form
onSubmit={(e)=> {
e.preventDefault();
if (!text.trim()) {
return;
}
addTodo(text);
setText('');
}}
>
<inputvalue={text}onChange={e=>setText(e.target.value)} />
<buttontype="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/

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


以上所述就是小编给大家介绍的《Redux+Hook 重写 Todo List,用代码实例挽回摒弃 Redux 的用户》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Growth Hack 這樣做

Growth Hack 這樣做

Xdite / PCuSER電腦人文化 / 2016-5-7 / 300.00台幣

◎具體教你在預算有限的情況之下,把成長做出來的可行與必要方法! ◎帶動台灣成長駭客話題的專業講師,親授讓產品突破80分的成長秘笈 @這本書要給誰看? 1. 創業者、個人品牌經營者,想要提高自己服務轉換率的人。 2. 空有產品,但是賣不出去,花了錢投廣告卻效果低落的人。 @這本書有什麼不一樣? 1.全球最重要的趨勢,台灣最知名的 Growth Hack 講師 Xd......一起来看看 《Growth Hack 這樣做》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试