使用Facebook的Recoil创建您的第一个CRUD

2020年12月30日11:09:12 发表评论 47 次浏览

本文概述

Redux到目前为止, 它一直是React应用程序状态管理的领导者。 GitHub上有超过53K颗星, 它成熟, 强大并且受到社区的大力支持。

Recoil是Facebook团队针对同一问题的新选择。在与Recoil合作后, 我的主要感受之一就是完全处于React世界中(不是很奇怪)的感觉。学习曲线低于Redux和其他类似的库, 这可能是由于其简单性。

反冲基于原子和选择器。简而言之, 原子是存储状态值的全局单位, 而选择器是从全局存储中检索计算值的"获取器"。

但是, 我们的重点不是详细解释lib。你可以读到这里—并且应该。

我们的目标是在实践中探索Recoil的一些主要功能。为此, 让我们使用React创建功能齐全的产品CRUD, 创建反应应用, 反应引导(以方便进行设计和填充), 以及用于状态管理的Recoil。

在文章结尾, 这将是我们的CRUD应用程序:

我们完成的CRUD应用

因此, 让我们直接讲吧。

设定

首先, 你应该检查是否已经安装了Node。在本教程中, 我们将使用Yarn命令(可选), 因此最好安装了它也一样在此示例中, 我们还将使用VS Code作为IDE。

通过命令行进入你选择的文件夹并运行:

我们为制作了一个自定义演示.
不完全是。点击这里查看.

使用Facebook的Recoil创建您的第一个CRUD1
npx create-react-app recoil-products-crud 
cd recoil-products-crud

遵循默认选项, 直到最后。现在, 让我们安装所需的依赖项。为此, 请运行以下命令:

yarn add recoil bootstrap react-bootstrap uuid

的uid模块将帮助我们为产品列表生成随机ID。

由于Bootstrap将负责我们页面元素的设计, 因此我们将其CSS导入添加到index.js文件, 以及其余的初始反冲设置:

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { RecoilRoot } from "recoil";
import * as serviceWorker from "./serviceWorker";

import "bootstrap/dist/css/bootstrap.min.css";

ReactDOM.render(
    <React.StrictMode>
        <RecoilRoot>
            <App />
        </RecoilRoot>
    </React.StrictMode>, document.getElementById("root")
);

serviceWorker.unregister();

反应应用程式必须始终被包裹反冲根标签以启用反冲功能。

项目的最终结构如下所示(请确保在你的文件中重现该结构):

recoil-products-crud
|----- src
        |----- components
      |----- AddProduct.js
      |----- ListProducts.js
      |----- FilterProduct.js
      |----- modals
          |----- EditModal.js
          |----- DeleteModal.js
        |----- store
      |----- atom.js
      |----- index.js
      |----- selector.js
|----- index.js
|----- App.js
|----- serviceWorker.js

后座力设定

我们的应用将提供四个默认的CRUD操作, 并能够按产品名称过滤结果。这将使我们能够探索选择器功能。

因此, 我们需要两个不同的原子:一个用于存储已注册产品的原始列表, 另一个用于在执行过滤操作后保存已过滤列表的状态。

让我们从atom.js代码如下:

import { atom } from "recoil";

export const products = atom({
  key: "products", default: [], });

export const filterProductsValue = atom({
  key: "filterProductsValue", default: "", });

到目前为止非常简单。我们只是创建了两个需要的原子, 每个原子都有一个唯一的键和默认值(目前都为空)。

接下来, 继续选择器内容:

import { selector } from "recoil";
import { products, filterProductsValue } from ".";

export const filterProducts = selector({
  key: "filterProducts", get: ({ get }) => {
    const productsState = get(products);
    const filterProductsValueState = get(filterProductsValue);

    if (filterProductsValueState.length) {
      return productsState.filter(
        (item) => item.name.includes(filterProductsValueState.trim()) && item
      );
    }
    return products;
  }, });

在这里, 我们正在导入选择器反冲功能。请注意, 选择器还需要一个唯一键。他们作为国家价值观的搜寻者。的得到value接收过滤代码, 在我们的示例中, 该代码将首先恢复产品列表和键入的过滤器文本, 最后在前一个列表中执行搜索。

对于你有许多不同的搜索需求, 可以创建许多新的选择器。如果选择器的代码太大, 则应考虑将它们组织在不同的JavaScript文件中。

让我们以用于index.js:

export { products, filterProductsValue } from "./atom";
export { filterProducts } from "./selector";

在这里, 我们只是将原子和选择器统一在一个地方。每当你需要它们时, 只需导入商店.

组成部分

显然, 没有React组件, 我们的Recoil代码将无法单独运行。因此, 让我们继续他们的构建。我们将从较低的组件(模式)到较高的组件进行此操作。

该示例将有两种模式:一种用于产品的编辑, 另一种用于产品的删除。这是代码DeleteModal.js文件:

import React from "react";
import { Button, Modal } from "react-bootstrap";
import { useRecoilState } from "recoil";

import { products } from "../../store";

const DeleteModal = (props) => {
  const { show, id, handleClose } = props;

  const [productList, setProductList] = useRecoilState(products);
  const product = productList.length
    ? productList.find((item) => item.id === id)
    : null;
  const index = productList.findIndex((item) => item === product);

  const deleteProduct = () => {
    setProductList(removeProduct(productList, index));
    handleClose();
  };

  return (
    <>
      {product ? (
        <Modal show={show} onHide={handleClose}>
          <Modal.Header closeButton>
            <Modal.Title>Delete the Product</Modal.Title>
          </Modal.Header>
          <Modal.Body>Are you sure?</Modal.Body>
          <Modal.Footer>
            <Button variant="secondary" onClick={() => handleClose()}>
              Close
            </Button>
            <Button variant="primary" onClick={() => deleteProduct()}>
              Yes, Do it.
            </Button>
          </Modal.Footer>
        </Modal>
      ) : (
        ""
      )}
    </>
  );
};

function removeProduct(products, i) {
  return [...products.slice(0, i), ...products.slice(i + 1)];
}

export default DeleteModal;

此模态有两个属性作为属性:布尔值, 用于确定模态是否应显示;以及id选择进行编辑的用户数量。

注意使用useRecoilState功能。相当于React的useState, 分别检索该州的产品列表及其设置者功能。

之后, 我们将循环浏览产品列表, 以根据在道具, 以及编号在同一阵列中。这些值将有助于其他功能。

的deleteProduct函数简单地在后坐状态中替换由removeProduct功能。反过来, 给定作为参数传递的索引, 该数组又从数组中消除了相应的乘积。

确保通过以下方式完成模态目标后始终关闭模态处理关闭功能。

现在, 进入第二个模式EditModal.js:

import React, { useState } from "react";
import { Button, Form, Modal } from "react-bootstrap";
import { useRecoilState } from "recoil";
import { products } from "../../store";

const EditModal = (props) => {
  const { show, id, handleClose } = props;

  const [productList, setProductList] = useRecoilState(products);
  const product = productList.length
    ? productList.find((item) => item.id === id)
    : null;
  const index = productList.findIndex((item) => item === product);

  const [name, setName] = useState("");
  const [color, setColor] = useState("");
  const [size, setSize] = useState(0.0);
  const [quantity, setQuantity] = useState(0);

  const onChangeName = (e) => {
    setName(e.target.value);
  };

  const onChangeColor = (e) => {
    setColor(e.target.value);
  };

  const onChangeSize = (e) => {
    setSize(e.target.value);
  };

  const onChangeQuantity = (e) => {
    setQuantity(e.target.value);
  };

  const updateProduct = () => {
    const newList = replaceProduct(productList, index, {
      ...product, name: name, color: color, size: size, quantity: quantity, });

    setProductList(newList);
    handleClose();
  };

  return (
    <>
      {product ? (
        <Modal show={show} onHide={handleClose}>
          <Modal.Header closeButton>
            <Modal.Title>Edit the Product</Modal.Title>
          </Modal.Header>
          <Modal.Body>
            <Form>
              <Form.Group controlId="name">
                <Form.Label>Name</Form.Label>
                <Form.Control
                  type="text"
                  placeholder="Enter the Product Name"
                  defaultValue={product.name}
                  onChange={(e) => onChangeName(e)}
                />
              </Form.Group>

              <Form.Group controlId="color">
                <Form.Label>Color</Form.Label>
                <Form.Control
                  type="text"
                  placeholder="Enter the Product Color"
                  defaultValue={product.color}
                  onChange={(e) => onChangeColor(e)}
                />
              </Form.Group>

              <Form.Group controlId="size">
                <Form.Label>Size</Form.Label>
                <Form.Control
                  type="number"
                  placeholder="Enter the Product Size"
                  defaultValue={product.size}
                  onChange={(e) => onChangeSize(e)}
                />
              </Form.Group>

              <Form.Group controlId="quantity">
                <Form.Label>Quantity</Form.Label>
                <Form.Control
                  type="number"
                  placeholder="Enter the Product Quantity"
                  defaultValue={product.quantity}
                  onChange={(e) => onChangeQuantity(e)}
                />
              </Form.Group>
            </Form>
          </Modal.Body>
          <Modal.Footer>
            <Button variant="secondary" onClick={() => handleClose()}>
              Close
            </Button>
            <Button variant="primary" onClick={() => updateProduct()}>
              Save Changes
            </Button>
          </Modal.Footer>
        </Modal>
      ) : (
        ""
      )}
    </>
  );
};

function replaceProduct(products, i, newVal) {
  return [...products.slice(0, i), newVal, ...products.slice(i + 1)];
}

export default EditModal;

它的大部分内容与表单组成有关。在我们的表单中有四个字段, 并且每个字段都必须具有局部状态表示形式(通过Hooks)作为常量。这样, 我们可以在将它们的值发送到Recoil状态之前对其进行操作。

每个字段还必须具有其对应的onChangeXX函数, 该函数将根据用户键入的内容刷新它们的值。请注意, 就像在其他模式中一样, 我们还引用同一产品的列表, 索引和设置器函数。

的更新产品该功能通过产生新列表(用户在模态字段中输入的更新值)并将其设置为反冲状态来起作用。为此, 它使用了辅助功能replaceProduct, 位于列表末尾。

列出产品

我们应用程序中的列表列将负责显示模式, 这些模式又将由每个相应的操作按钮触发。

这是内容ListProduct.js文件:

import React, { useState } from "react";
import { Table, Button } from "react-bootstrap";

import { useRecoilValue, useResetRecoilState } from "recoil";
import { filterProducts, filterProductsValue, products } from "../store";
import EditModal from "./modals/EditModal";
import DeleteModal from "./modals/DeleteModal";

const ListProducts = () => {
  const productsState = useRecoilValue(filterProducts);
  const resetList = useResetRecoilState(products);
  const resetfilterProductsValue = useResetRecoilState(filterProductsValue);

  // Modals
  const [showEdit, setShowEdit] = useState(false);
  const [showDelete, setShowDelete] = useState(false);
  const [userId4Actions, setUserId4Actions] = useState(0);

  const handleEditClose = () => setShowEdit(false);
  const handleEditShow = () => setShowEdit(true);

  const handleDeleteClose = () => setShowDelete(false);
  const handleDeleteShow = () => setShowDelete(true);

  const resetAtoms = () => {
    resetList();
    resetfilterProductsValue();
  };

  const editProduct = (id) => {
    handleEditShow();
    setUserId4Actions(id);
  };

  const deleteProduct = (id) => {
    handleDeleteShow();
    setUserId4Actions(id);
  };

  return (
    <>
      <h3>List</h3>

      <Table striped bordered hover responsive>
        <thead>
          <tr>
            <th>Name</th>
            <th>Color</th>
            <th>Size</th>
            <th>Quantity</th>
            <th>Actions</th>
          </tr>
        </thead>
        <tbody>
          {!productsState.length ? (
            <tr>
              <td colSpan="5" style={{ textAlign: "center" }}>
                No products here.
              </td>
            </tr>
          ) : (
            productsState.map((item, index) => (
              <tr key={item.id}>
                <td>{item.name}</td>
                <td>{item.color}</td>
                <td>{item.size}</td>
                <td>{item.quantity}</td>
                <td colSpan="2">
                  <Button
                    variant="secondary"
                    onClick={() => editProduct(item.id)}
                  >
                    Edit
                  </Button>{" "}
                  {" | "}
                  <Button
                    variant="danger"
                    onClick={() => deleteProduct(item.id)}
                  >
                    Delete
                  </Button>
                </td>
              </tr>
            ))
          )}
        </tbody>
      </Table>

      <Button variant="secondary" size="lg" onClick={() => resetAtoms()}>
        Clear Data
      </Button>

      <EditModal
        show={showEdit}
        id={userId4Actions}
        handleClose={handleEditClose}
      />

      <DeleteModal
        show={showDelete}
        id={userId4Actions}
        handleClose={handleDeleteClose}
      />
    </>
  );
};

export default ListProducts;

这里的主要目标是在Bootstrap表中展示产品列表。请注意, 此处我们通过以下方式以不同的方式从反冲状态恢复产品列表:useRecoilValue功能。它仅返回存储中的当前值, 而没有设置器。下面的另外两个用于重置状态值。

接下来, 我们有模态展示和隐藏的辅助常数。我们将在列表代码中对此进行功能管理, 并通过prop传递各个值。

然后, 我们具有三个主要功能:

  1. resetAtoms–顾名思义, 它们利用Recoil重置功能将列表和搜索文本重置为初始值
  2. 编辑产品–切换编辑模式并将当前用户的ID设置为本地状态
  3. deleteProduct–切换删除模式并将当前用户的ID设置为本地状态

内渲染功能, 请注意, 我们也在检查产品列表是否存在, 否则将显示正确的消息。

新增表格

现在是时候实施添加产品组件, 它将使用表单中的产品值来填充列表。

这些是内容AddProduct.js文件:

import React, { useState } from "react";
import { Button, Form } from "react-bootstrap";

import { useSetRecoilState } from "recoil";
import { v4 as uuid4 } from "uuid";
import { products } from "../store";

const AddProduct = () => {
  const [name, setName] = useState("");
  const [color, setColor] = useState("");
  const [size, setSize] = useState(0.0);
  const [quantity, setQuantity] = useState(0);
  const setProducts = useSetRecoilState(products);

  const onChangeName = (e) => {
    setName(e.target.value);
  };

  const onChangeColor = (e) => {
    setColor(e.target.value);
  };

  const onChangeSize = (e) => {
    setSize(e.target.value);
  };

  const onChangeQuantity = (e) => {
    setQuantity(e.target.value);
  };

  const addProduct = () => {
    setProducts((oldList) => [
      ...oldList, {
        id: uuid4(), name: name, color: color, size: size, quantity: quantity, }, ]);

    resetForm();
  };

  const resetForm = () => {
    setName("");
    setColor("");
    setSize(0.0);
    setQuantity(0);
  };

  return (
    <Form>
      <Form.Group controlId="name">
        <Form.Label>Name:</Form.Label>
        <Form.Control
          type="text"
          value={name}
          placeholder="Enter the Product Name"
          onChange={(e) => onChangeName(e)}
        />
      </Form.Group>

      <Form.Group controlId="color">
        <Form.Label>Color:</Form.Label>
        <Form.Control
          type="text"
          value={color}
          placeholder="Enter the Product Color"
          onChange={(e) => onChangeColor(e)}
        />
      </Form.Group>

      <Form.Group controlId="size">
        <Form.Label>Size:</Form.Label>
        <Form.Control
          type="number"
          value={size}
          placeholder="Enter the Product Size"
          onChange={(e) => onChangeSize(e)}
        />
      </Form.Group>

      <Form.Group controlId="quantity">
        <Form.Label>Quantity:</Form.Label>
        <Form.Control
          type="number"
          value={quantity}
          placeholder="Enter the Product Quantity"
          onChange={(e) => onChangeQuantity(e)}
        />
      </Form.Group>

      <Button variant="primary" size="lg" onClick={() => addProduct()}>
        Add
      </Button>
    </Form>
  );
};

export default AddProduct;

创建表单与编辑模式非常相似, 因为此处也发生了相同的字段。我们有一样onChange功能以及本地状态属性。

功能addProduct添加一个新产品, 将更新后的数组设置为Recoil状态, 然后重置表格。

到现在为止, 根据先前组件的知识, 这里没有太多要添加的内容。

过滤产品

过滤器组件也非常简单。它由一个用于收集过滤器文本的文本字段和一个用于清除过滤器文本的按钮组成。过滤将在对该字段进行任何更改的情况下执行, 而不是单击普通按钮。

这是FilterProducts.js文件:

import React from "react";
import { useRecoilState } from "recoil";

import { filterProductsValue } from "../store";
import { Form, Button } from "react-bootstrap";

const FilterProducts = () => {
  const [filterProductsState, filterProducts] = useRecoilState(
    filterProductsValue
  );

  const filter = (event) => {
    const { value } = event.target;
    filterProducts(value);
  };

  const clearFilter = () => filterProducts("");

  return (
    <Form>
      <Form.Group controlId="name">
        <Form.Label>Filter:</Form.Label>
        <Form.Control
          type="text"
          placeholder="Filter by Product Name"
          value={filterProductsState}
          onChange={(e) => filter(e)}
        />
      </Form.Group>

      <Button variant="info" onClick={() => clearFilter()}>
        Clear Filter
      </Button>
    </Form>
  );
};

export default FilterProducts;

最后, 我们只需要在App.js文件, 应用程序的根目录。

将以下代码放入其中:

import React from "react";
import Container from "react-bootstrap/Container";
import { Row, Col } from "react-bootstrap";

import AddProduct from "./components/AddProduct";
import FilterProducts from "./components/FilterProducts";
import ListProducts from "./components/ListProducts";

function App() {
  return (
    <Container className="p-3">
      <div className="py-5 text-center">
        <h1>Product's CRUD</h1>
        <p className="lead">
          Go ahead and play with a CRUD made with React, Bootstrap and Recoil.js
        </p>
      </div>
      <Row>
        <Col>
          <AddProduct />
        </Col>
        <Col className="col-8">
          <FilterProducts />
          <hr />
          <ListProducts />
        </Col>
      </Row>
    </Container>
  );
}

export default App;

这里没什么特别的, 只是普通的React代码。随意使用组件的配置, 只是要知道每个组件将如何基于react-bootstrap CSS类适应屏幕。

总结

就是这样-现在是你尝试该示例的时候了。只要照常运行npm开始并检查在浏览器中运行的示例。

同样, Recoil代码中对React的这种嗅探对库来说是一个很大的好处, 特别是如果你已经在使用React Hooks。

事实上, 别忘了检查官方文档出来。他们不仅会引导你, 而且会在每个主要版本发布后带领你了解库的工作方式, 以及如何从中最大程度地受益。

你可以找到此示例的代码这里在我的GitHub中。

全面了解生产React应用

调试React应用程序可能很困难, 尤其是当用户遇到难以重现的问题时。如果你有兴趣监视和跟踪Redux状态, 自动显示JavaScript错误以及跟踪缓慢的网络请求和组件加载时间,

尝试notlogy

.

使用Facebook的Recoil创建您的第一个CRUD2
LogRocket仪表板免费试用横幅

日志火箭就像Web应用程序的DVR, 实际上记录了React应用程序中发生的一切。你可以汇总并报告问题发生时应用程序所处的状态, 而不用猜测为什么会发生问题。 notlogy还监视你的应用程序的性能, 并使用客户端CPU负载, 客户端内存使用情况等指标进行报告。

notlogy Redux中间件软件包在你的用户会话中增加了一层可见性。 notlogy记录Redux存储中的所有操作和状态。

现代化如何调试React应用程序-免费开始监控.

一盏木

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: