作为React应用的状态管理库,Recoil的入门教程
在你的Web应用中管理状态是至关重要的,因为它有助于确保不同页面上显示的数据是一致的。React默认提供了useState() 钩子,我们可以用它来存储和修改应用程序的状态。
这个钩子的缺点是,我们必须把状态从一个组件传递到另一个组件,随着项目变得越来越广泛,传递太多的道具可能会变得很忙。
状态管理库通过创建一个全局存储来解决这个问题,每个组件都可以访问它需要的数据。有了状态管理库,数据从你的应用程序流向状态,反之亦然,你可以从任何组件访问数据,而不需要将其作为道具传递。
我们将通过步骤来使用Recoil来管理我们的React Web应用程序的状态。然后我们将创建一个Todo应用程序,允许用户添加任务,将其标记为完成,删除它们,并在已完成和未完成的任务之间进行过滤。
前提条件
需要以下条件才能跟上。
- 对React的中级知识,一个用于构建用户界面的JavaScript库。
- 了解React功能组件和
useState钩子。 - 一个代码编辑器和一个浏览器。
目标
在本文结束时,你应该能够。
- 使用Recoil在你的React应用中管理状态。
- 对Recoil的原子和选择器有所了解。
开始学习
在我们开始之前,我们需要熟悉以下术语。
- 一个
atom- 原子是一块状态。我们可以将一个原子导入我们的组件中,这样我们就可以从我们的组件中使用和更新它。
每次对原子进行更新时,使用该原子的组件就会以更新的值重新显示。
一个原子的创建如下图所示。
const atomName = atom({
key: 'atomName',
default: <defaultValue>,
});
每个原子都应该有一个必须是唯一的键和一个默认值。
- 一个
selector- 一个选择器是一个函数。像普通函数一样,它接受输入(在这里是指其他选择器和原子)并给出输出。它被用来获取基于一个状态的衍生数据。
选择器的定义如下所示。
const selectorName = selector({
key: 'selectorName',
get: ({get}) => {
// code goes in here
},
});
每个选择器也有一个唯一的键和一个get 属性,即要计算的函数。要访问选择器的输入值,需要使用get 关键字。
创建我们的项目
要创建我们的应用程序,cd 到你想创建项目的目录,在终端运行以下命令。
npx create-react-app react-recoil-todoapp
cd react-recoil-todoapp
code .
这将创建一个React应用程序。继续并在你的代码编辑器中打开它。
接下来,打开终端并运行以下命令,将recoil作为一个依赖项安装。
npm install recoil
创建recoil文件夹
在src 文件夹中创建一个新的文件夹,并将其命名为recoil 。在该文件夹中,创建一个新的文件,并将其称为atom.js 。我们将在这个文件中创建我们的原子和选择器。
将下面给出的代码输入到atom.js 文件中。
//import atom and selctor from the recoil package through object destructuring
import { atom, selector } from "recoil";
//create an atom that will be used to store all tasks entered by the user.
const allTasks = atom({
key: "allTasks",
default: []
})
//create an atom that will be used to toggle between different values in the filtered tasks selector
const tasksFilter = atom({
key: "tasksFilter",
default: "Show All"
});
//create a selector to help in toggling between all, completed and uncompleted tasks
const filteredTasks = selector({
key: "filteredTasks",
get: ({ get }) => {
const filter = get(tasksFilter);
const list = get(allTasks);
switch (filter) {
case "Show Completed":
return list.filter((item) => item.isDone);
case "Show Uncompleted":
return list.filter((item) => !item.isDone);
default:
return list;
}
}
});
//export our atoms and selector
export {
allTasks,
tasksFilter,
filteredTasks
}
在上面的代码中,我们首先从recoil导入原子和选择器。然后我们创建我们的allTasks 原子,它将存储用户添加的任务。
我们创建tasksFilter 原子,在过滤所有已完成和未完成的任务时,它将帮助filteredTasks 选择器。然后,我们创建一个filteredTasks 选择器,访问我们的原子并将它们存储在常量中(filter 和list )。
然后,它使用switch 语句来返回一个基于所选标准的任务列表。在我们的例子中,完成的、未完成的和所有的(所有的由默认值表示,它返回整个任务的列表)。
创建我们的组件
为了创建我们的组件,在src 文件夹中,创建一个新的文件夹并命名为components 。
在这个组件文件夹中,创建四个文件,即。
Input.js- 这将包含我们将用于添加新任务的表格。Tasks.js- 这将包含一个所有任务的列表。Task.js- 这将代表每个任务。TaskFilters.js- 这将包含一个下拉菜单,我们将用它来选择所有已完成和未完成的任务。
创建我们的todo应用程序
现在我们已经设置好了一切,打开App.css 文件并粘贴下面的代码以应用基本的风格。
.App{
flex-direction: column;
display: flex;
align-items: center;
justify-content: center;
}
接下来,打开App.js 文件并粘贴下面的代码。
import { RecoilRoot } from "recoil";
import './App.css';
import Input from "./components/Input";
import TaskFilters from "./components/TaskFilters";
import Tasks from "./components/Tasks";
function App() {
return (
<div className="App">
<h2>Todo App with React and Recoil</h2>
<RecoilRoot>
<TaskFilters />
<Input />
<Tasks />
</RecoilRoot>
</div>
);
}
export default App;
我们导入RecoilRoot ,并在上面的代码中把我们的整个应用程序(需要访问状态的组件)包裹起来。它作为一个提供者,使我们所有的组件可以访问原子和选择器。
在添加任务功能方面的工作
在Input.js 文件中,使用下面的代码。这将有助于向我们的原子添加新任务。
import React, { useState } from 'react'
import { useSetRecoilState } from 'recoil'
import { allTasks } from '../recoil/atom'
const Input = () => {
//Track the value of the input field
const [input, setInput] = useState("")
//Use the useSetRecoilState hook to update the allTasks atom
const setTasks = useSetRecoilState(allTasks);
//function to be called on the click of the add button.
const addTask = (e) => {
//prevent default form behavior on the click of add button
e.preventDefault();
//update the allTasks atom with the contents of the input field
setTasks((oldTasks) => [
...oldTasks, {
id: Math.floor(Math.random() * 1000), //generate a random id for the new task
text: input,
isDone: false //set task completion to false by default
}
])
setInput("") //clear the contents of the input field
}
return (
<div>
{/* create a form that will be used to add a task */}
<form>
<input type="text" value={input} onChange={(e) => setInput(e.target.value)} />
{/* Disable the button when the input field is empty */}
<button type='submit' disabled={!input} onClick={(e) => addTask(e)}>Add</button>
</form>
</div>
)
}
export default Input
在上面的代码中,我们导入了ReactuseState 钩子,这将有助于我们跟踪用户输入的值。我们导入了 RecoiluseSetRecoilState 钩子,这将允许我们向我们的allTasks 原子添加任务,我们也在文件中导入了这个钩子。
我们还创建了一个常量setTasks = useSetRecoilState(allTasks); ,它给了我们一个函数,我们可以用它来修改我们的allTasks 原子。在我们的return 部分,我们有一个form ,其中包含一个输入字段和一个按钮,当点击时调用addTask() 函数。
addTask() 函数使用我们先前创建的setTasks 函数来向我们的原子添加新项目。setTasks 函数接收我们原子中先前包含的项目的值,并返回一个包含新添加项目的数组。
从原子中读取数据
使用下面Tasks.js 中提供的代码。
import React from 'react'
import { useRecoilValue } from "recoil";
import { filteredTasks } from '../recoil/atom';
import Task from './Task';
function Tasks() {
//read the default return value(which is a list of all tasks) of the filtered tasks selector and assign it to a constant tasks
const tasks = useRecoilValue(filteredTasks);
return (
<div>
//map through the tasks array and call the Task component for each element. Also pass the value of each element to the Task component
{tasks.map((task, index) => (
<Task task={task} key={index} />
))}
</div>
)
}
export default Tasks
在上面的代码中,我们已经导入了useRecoilValue ,以便从原子中读取数据。此外,我们还导入了包含我们想要访问的数据的filteredTasks 选择器。
使用const tasks = useRecoilValue(filteredTasks); ,我们可以读取filteredTasks 选择器中包含的数据,并将其存储在一个名为tasks 的常量中。filteredTasks 选择器的默认返回值列出了所有任务。
在return 部分,我们已经通过tasks 数组进行了映射,并将数据传递给我们的Task 组件。
接下来,打开Task.js 文件,粘贴下面的代码。
import React from 'react'
import { useRecoilState } from 'recoil';
import { allTasks } from '../recoil/atom';
const Task = ({ task }) => {
const [tasks, setTasks] = useRecoilState(allTasks);
const index = tasks.findIndex((taskItem) => taskItem === task);
const replaceItemAtIndex = (arr, index, newValue) => {
return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)];
};
const removeItemAtIndex = (arr, index) => {
return [...arr.slice(0, index), ...arr.slice(index + 1)];
};
const toggleTaskCompletion = () => {
const newTasks = replaceItemAtIndex(tasks, index, {
...task,
isDone: !task.isDone
});
setTasks(newTasks)
};
//Delete a task
const deleteTask = (id) => {
const newTasks = removeItemAtIndex(tasks, index);
setTasks(newTasks);
}
return (
<div>
<span>{task.text}</span>
<input
type="checkbox"
checked={task.isDone}
onChange={toggleTaskCompletion}
/>
<button onClick={deleteTask}>X</button>
</div>
)
}
export default Task
这个组件接受从Tasks.js 文件中通过对象析构传递的道具。使用传来的道具,我们返回一个包含文本的span ,一个我们可以用来切换项目完成情况的复选框,以及一个我们可以用来删除任务的按钮。
复选框的检查值是基于任务被标记为完成或未完成。在点击复选框的时候,我们要调用toggleTaskCompletion() 函数。
toggleTaskCompletion() 调用replaceItemAtIndex() ,并传递任务数组、被点击的项目的索引以及被点击的项目本身(该项目使用spread运算符铺开,以便其内容可以被修改。展开后,isDone 的当前值被附加)作为参数。
我们从这个函数中得到项目的索引,const index = tasks.findIndex((taskItem) => taskItem === task) 。replaceItemAtIndex() 函数接收传给它的参数,并返回一个用slice方法修改的数组。
toggleTaskCompletion() 将从replaceItemAtIndex() 函数收到的数组存储在名为newTasks 的常量中。然后它调用setTasks() 函数并传递给newTasks 。
setTasks() 函数用新的数组更新我们的原子。随着删除按钮的点击,我们正在调用deleteTask() 方法。该方法调用removeItemAtIndex() 函数并传递任务数组和被点击的项目索引作为参数。
removeItemAtIndex() 接收传递给它的参数,并返回一个使用JavaScript的slice() 方法修改的数组。deleteTask() 将返回的数组存储在一个名为newTasks 的常量中。然后它调用setTasks() 函数并传递newTasks,更新我们的allTasks 原子。
实现过滤器
我们现在可以添加、删除和切换项目完成,我们可以实现过滤器,这将帮助我们根据特定的标准显示任务。
要做到这一点,打开TaskFilters.js 文件并粘贴下面的代码。
import React from 'react'
import { useRecoilState } from "recoil";
import { tasksFilter } from '../recoil/atom';
const TaskFilters = () => {
const [filter, setFilter] = useRecoilState(tasksFilter);
return (
<div>
Filter:
<select value={filter} onChange={(e) => setFilter(e.target.value)}>
<option value="Show All">All</option>
<option value="Show Completed">Completed</option>
<option value="Show Uncompleted">Uncompleted</option>
</select>
</div>
)
}
export default TaskFilters
在上面的代码中,我们有一个有三个选项的下拉菜单。在改变选项时,我们将当前选择的选项的值传递给引用tasksFilter 原子的setFilter() 函数。
在传递过滤器时,filteredTasks 选择器中的开关语句将过滤器与定义的案例相匹配,并只返回符合预定义标准的任务。
运行我们的应用程序
要运行该应用程序,打开集成终端并运行以下命令。
npm start
在你的浏览器上,打开链接localhost:3000, 。你可以添加任务,删除任务,将其标记为完成或未完成,并在已完成、未完成和所有任务之间进行过滤。
结论
这是在我们的React应用中对Recoil的一个基本实现。在更好地理解上面讨论的概念后,你可以在你的项目中实现Recoil。
你也可以在这个项目上下功夫,把它提升到更高的水平。