React + TypeScript: 通过 Recoil 公式教程创建 Todo 列表 01 – 使用 atom 进行项目操作

    • 「React + TypeScript: Recoil公式チュートリアルのTodoリストをつくる 02 ー selectorによるフィルタリングと集計」

 

    • 「React + TypeScript: Recoil公式チュートリアルのTodoリストをつくる 03 ー 構成とロジックを整える」

 

    「React + TypeScript: Recoil公式チュートリアルのTodoリストをつくる 04 ー 状態を直接exportしないようにする」

Recoil是由Meta(Facebook的子公司)开发的用于管理React状态的库。它不是将状态整合到一起,而是细分并单独处理每一个状态。在文章《使用React + TypeScript:尝试使用Facebook的状态管理库Recoil》中,我们通过简单的代码示例解释了它的大致原理。

本系列的主题是基于Recoil官方的”Basic Tutorial”中的示例,一个名为Todo List的任务清单。不过,我们对代码进行了模块分离,并加入了通过TypeScript进行类型定义的功能。本文首次介绍的是使用atom来进行列表项的添加、编辑和删除操作(示例001)。

样本001■React + TypeScript:Recoil教程示例01

 

确定根组件

使用React应用程序的模板,可以使用Create React App创建。您可以阅读”使用Create React App创建带有TypeScript的模板应用程序”来了解如何创建。

想要共享状态的组件树根节点必须包裹在中(参考”用包裹组件树”)。通过这样做,所有子孙组件都可以使用相同的状态。父组件将在稍后定义为TodoList(代码003)。

代码001■待办事项清单应用程序组件。

import { RecoilRoot } from 'recoil';
import { TodoList } from './TodoList';

export default function App() {
	return (
		<RecoilRoot>
			<TodoList />
		</RecoilRoot>
	);
}

用atom来确定Todo清单的状态

atom是一个可信的源(source of truth),用于表示应用程序的状态 (参见 “可信的唯一信息源”)。在todoListState中,使用函数atom()来定义Todo项(TodoItemType类型)的列表(数组)数据(状态)。给定的参数选项对象中的key是状态的唯一标识符 (todoListState),default是状态的初始值 (TodoItemType[]类型的空数组)。

用以下的代码002■来确定待办事项列表的数据

import { atom } from 'recoil';

export type TodoItemType = {
	id: number;
	text: string;
	isComplete: boolean;
};
export const todoListState = atom<TodoItemType[]>({
	key: 'todoListState',
	default: [],
});

使用 useRecoilValue 钩子 来读取状态

useRecoilValue()是一个从参数atom中读取值的钩子。与前文中使用的useRecoilState()不同,它不返回设置函数(也就是说只能读取值,无法进行设置)。

TodoList组件将从提取的Todo列表数组(todoList)中按顺序提供元素(todoItem)的数据给定项的组件TodoItem来进行列表显示。

代码003■Todo列表的父组件

import type { FC } from 'react';
import { useRecoilValue } from 'recoil';
import { TodoItem } from './TodoItem';
import { TodoItemCreator } from './TodoItemCreator';
import { todoListState } from './todoListState';

export const TodoList: FC = () => {
	const todoList = useRecoilValue(todoListState);
	return (
		<>
			<TodoItemCreator />
			{todoList.map((todoItem) => (
				<TodoItem key={todoItem.id} item={todoItem} />
			))}
		</>
	);
};

使用 useSetRecoilState 钩子来设置状态值。

TodoItemCreator是一个组件,它将作为属性传递给使用useSetRecoilState()钩子函数来设置状态的atom的状态,并将元素中输入的文本作为Todo项目添加到todoListState状态中(Code 004)。返回值setTodoList是状态设置函数。

调用状态设置函数setTodoList使用了函数式的更新方法,传入的参数是一个函数,该函数接收旧的Todo列表oldTodoList作为参数,并返回新的值。新的Todo项将被添加到当前的Todo列表todoListState中。

将待办事项列表项添加到数据中的组件:Code 004。

import { useCallback, useState } from 'react';
import type { ChangeEventHandler, FC } from 'react';
import { useSetRecoilState } from 'recoil';
import { todoListState } from './todoListState';

// utility for creating unique Id
let id = 0;
const getId = () => {
	return id++;
}
export const TodoItemCreator: FC = () => {
	const [inputValue, setInputValue] = useState('');
	const setTodoList = useSetRecoilState(todoListState);
	const addItem = useCallback(() => {
		setTodoList((oldTodoList) => [
			...oldTodoList,
			{
				id: getId(),
				text: inputValue,
				isComplete: false,
			},
		]);
		setInputValue('');
	}, [inputValue, setTodoList]);
	const onChange: ChangeEventHandler<HTMLInputElement> = useCallback(
		({ target: { value } }) => {
			setInputValue(value);
		},
		[]
	);
	return (
		<div>
			<input type="text" value={inputValue} onChange={onChange} />
			<button onClick={addItem}>Add</button>
		</div>
	);
};

使用 useRecoilState 钩子来读取和写入状态的值

TodoItem组件用于显示从Todo列表状态(atom)数据中提取的每个项目。它包含一个复选框来表示是否已完成的状态,并且还有一个删除按钮来删除该项目。它还可以编辑(添加)项目文本。换句话说,必须能够读写状态的值。

用Recoil提供的钩子函数useRecoilState()可以读写atom的状态。其语法与useState相同,返回值数组的第一个元素(todoList)是状态变量,第二个元素(setTodoList)是设置函数。不同之处在于,Recoil的状态是在组件树中共享的。

Todo项目的数据由组件以参数对象(item)的形式接收,并定义了编辑(editItemText)、勾选(toggleItemCompletion)和删除(deleteItem)的函数,代码如下(参考代码005)。实际的操作效果,请查看之前发布在CodeSandbox上的示例001来确认。

用于显示、检查、编辑和删除代码005项目的组件

import { useCallback } from 'react';
import type { ChangeEventHandler, FC } from 'react';
import { useRecoilState } from 'recoil';
import { todoListState } from './todoListState';
import type { TodoItemType } from './todoListState';

type Props = {
	item: TodoItemType;
};
const replaceItemAtIndex = (
	arr: TodoItemType[],
	index: number,
	newValue: TodoItemType
) => {
	return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)];
};
const removeItemAtIndex = (arr: TodoItemType[], index: number) => {
	return [...arr.slice(0, index), ...arr.slice(index + 1)];
};
export const TodoItem: FC<Props> = ({ item }) => {
	const [todoList, setTodoList] = useRecoilState(todoListState);
	const index = todoList.findIndex((listItem) => listItem === item);
	const editItemText: ChangeEventHandler<HTMLInputElement> = useCallback(
		({ target: { value } }) => {
			const newList = replaceItemAtIndex(todoList, index, {
				...item,
				text: value,
			});
			setTodoList(newList);
		},
		[index, item, setTodoList, todoList]
	);
	const toggleItemCompletion = useCallback(() => {
		const newList = replaceItemAtIndex(todoList, index, {
			...item,
			isComplete: !item.isComplete,
		});
		setTodoList(newList);
	}, [index, item, setTodoList, todoList]);
	const deleteItem = useCallback(() => {
		const newList = removeItemAtIndex(todoList, index);
		setTodoList(newList);
	}, [index, setTodoList, todoList]);
	return (
		<div>
			<input type="text" value={item.text} onChange={editItemText} />
			<input
				type="checkbox"
				checked={item.isComplete}
				onChange={toggleItemCompletion}
			/>
			<button onClick={deleteItem}>X</button>
		</div>
	);
};
    • 「React + TypeScript: Recoil公式チュートリアルのTodoリストをつくる 02 ー selectorによるフィルタリングと集計」

 

    • 「React + TypeScript: Recoil公式チュートリアルのTodoリストをつくる 03 ー 構成とロジックを整える」

 

    「React + TypeScript: Recoil公式チュートリアルのTodoリストをつくる 04 ー 状態を直接exportしないようにする」
广告
将在 10 秒后关闭
bannerAds