react hooks使用_使用React Hooks和Context API构建CRUD应用

react hooks使用

This article will be covering React hooks, introduced in version 16.8 and the Context API, introduced in 16.3 and mixing them together to build a fully functional CRUD application. Here we won’t be using any external API calls but we’ll get our hands dirty with hard-coded objects which will serve as the state.

本文将介绍版本16.8中引入的React挂钩和版本16.3中引入的Context API ,并将它们混合在一起以构建功能全面的CRUD应用程序。 在这里,我们将不会使用任何外部API调用,但是会被硬编码的对象用作状态,这会弄脏我们的手。

The introduction of the Context API solves one major problem: prop drilling. The process of getting our data from one component to another through layers of nested deep components. Whereas Hooks helps us, React developers, by allowing us to use a functional rather than class-based components. Where we needed to utilize a lifecycle method, we had to use a class-based approach. And we now no longer have to call super(props) or worry about binding methods or the this keyword.

Context API的引入解决了一个主要问题: 道具钻探 。 通过嵌套的深层组件将数据从一个组件获取到另一组件的过程。 Hooks通过允许我们使用功能性组件而不是基于类的组件来帮助我们,React开发人员。 需要使用生命周期方法的地方,必须使用基于类的方法。 现在,我们不再需要调用super(props)或担心绑定方法或this关键字。

Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function.

有时,优雅的实现仅仅是一个功能。 不是方法。 不上课。 不是一个框架。 只是一个功能。

– John Carmack. Oculus VR CTO

–约翰·卡马克(John Carmack)。 Oculus VR CTO



Less talk more code. Let’s start coding 🐊🐊🐊

少说话多代码。 让我们开始编码🐊🐊🐊

As you’ll notice, we’ll be making use of Tailwind CSS for the styling of our app.

您会注意到,我们将使用Tailwind CSS来设计我们的应用程序样式。

Firstly we’ll start with setting up our React project using Create React App with the following command:

首先,我们将从使用以下命令使用Create React App设置React项目开始:

$ npx create-react-app react-contextAPI

We’ll now initialize our package.json file with yarn init - and make sure we have the following dependencies below:

现在,我们用yarn init初始化package.json文件,并确保下面具有以下依赖关系:

"react": "^16.13.0", 
"react-dom": "^16.13.0", 
"react-router-dom": "^5.1.2", 
"react-scripts": "3.4.0", 
"eslint": "^6.8.0", 
"tailwindcss": "^1.2.0"

To add react-router-dom and Tailwind CSS as dependencies, just run the following command:

要将react-router-dom和Tailwind CSS添加为依赖项,只需运行以下命令:

$ npm i react-router-dom tailwindcss

And now here are some components we’ll create in a components directory:

现在这是我们将在components目录中创建的一些组件:

  • Home.js

    Home.js
  • AddEmployees.js

    AddEmployees.js
  • EditEmployees.js

    EditEmployees.js
  • EmployeeList.js

    EmployeeList.js
  • Heading.js

    Heading.js

Import these main components inside your App component. We’ll also have to import Route and Switch from react-router-dom. And beforehand we will wrap our app with GlobalProvider which we need from GlobalState (which we’ll define later).

将这些主要组件导入App组件中。 我们还必须从react-router-dom导入RouteSwitch 。 事先我们将用GlobalProvider包装我们的应用程序,这是我们需要的GlobalState (我们将在以后定义)。

Here’s a quick intro to React Router if this is all new to you.

如果这是您的新手,这是React Router的快速入门。

Our App.js file will look something like this:

我们的App.js文件将如下所示:

import React from 'react';
import { Route, Switch } from 'react-router-dom';
import './stylesheet/styles.css';
import { Home } from './components/Home';
import { AddEmployee } from './components/Addemployee';
import { EditEmployee } from './components/Editemployee';


import { GlobalProvider } from './context/GlobalState';

function App() {
  return (
    <GlobalProvider>
      <Switch>
        <Route path="/" component={Home} exact />
        <Route path="/add" component={Addemployee} exact />
        <Route path="/edit/:id" component={Editemployee} exact />
      </Switch>
    </GlobalProvider>
  );
}

export default App;

We will now move forward with printing the list of Employees inside our EmployeeList.js file. The classNames that you’ll notice are Tailwind CSS utility classes and they help style our app.

现在,我们将继续在EmployeeList.js文件中打印雇员列表。 您将注意到的classNameTailwind CSS实用程序类 ,它们有助于设计我们的应用程序。

EmployeeList.js
EmployeeList.js
import React, { Fragment, useContext } from "react";
import { GlobalContext } from "../context/GlobalState";
import { Link } from "react-router-dom";

export const Employeelist = () => {
  const { employees, removeEmployee, editEmployee } = useContext(GlobalContext);
  return (
    <Fragment>
      {employees.length > 0 ? (
        <Fragment>
          {employees.map(employee => (
            <div
              className="flex items-center bg-gray-100 mb-10 shadow"
              key={employee.id}
            >
              <div className="flex-auto text-left px-4 py-2 m-2">
                <p className="text-gray-900 leading-none">{employee.name}</p>
                <p className="text-gray-600">{employee.designation}</p>
                <span className="inline-block text-sm font-semibold mt-1">
                  {employee.location}
                </span>
              </div>
              <div className="flex-auto text-right px-4 py-2 m-2">
                <Link to={`/edit/${employee.id}`}>
                  <button
                    onClick={() => editEmployee(employee.id)}
                    className="bg-gray-300 hover:bg-gray-400 text-gray-800 font-semibold mr-3 py-2 px-4 rounded-full inline-flex items-center"
                  >
                    <svg
                      xmlns="http://www.w3.org/2000/svg"
                      width="24"
                      height="24"
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      strokeWidth="2"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      className="feather feather-edit"
                    >
                      <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
                      <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
                    </svg>
                  </button>
                </Link>
                <button
                  onClick={() => removeEmployee(employee.id)}
                  className="block bg-gray-300 hover:bg-gray-400 text-gray-800 font-semibold py-2 px-4 rounded-full inline-flex items-center"
                >
                  <svg
                    xmlns="http://www.w3.org/2000/svg"
                    width="24"
                    height="24"
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    className="feather feather-trash-2"
                  >
                    <polyline points="3 6 5 6 21 6"></polyline>
                    <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                    <line x1="10" y1="11" x2="10" y2="17"></line>
                    <line x1="14" y1="11" x2="14" y2="17"></line>
                  </svg>
                </button>
              </div>
            </div>
          ))}
        </Fragment>
      ) : (
        <p className="text-center bg-gray-100 text-gray-500 py-5">No data</p>
      )}
    </Fragment>
  );
};

In the above code we imported GlobalState and useContext, one of the built-in React Hooks, giving functional components easy access to our context.

在上面的代码中,我们导入了GlobalStateuseContext ,它们是内置的React Hooks之一,使功能组件可以轻松访问我们的上下文。

Moreover, we imported our employees object, removeEmployee and editEmployees from our GlobalState.js file.

此外,我们从GlobalState.js文件中导入了employees对象, removeEmployee和editEmployees。

Let’s move on creating a GlobalState file where we will make our function inside of which w’ll dispatch our action.

让我们继续创建一个GlobalState文件,在该文件中我们将使函数在其中进行调度。

import React, { createContext, useReducer } from "react";
import AppReducer from "./AppReducer";

const initialState = {
  employees: [
    {
      id: 1,
      name: "Ishan Manandhar",
      location: "Kathmandu",
      designation: "Frontend Developer"
    }
  ]
};

export const GlobalContext = createContext(initialState);
export const GlobalProvider = ({ children }) => {
  const [state, dispatch] = useReducer(AppReducer, initialState);

  function removeEmployee(id) {
    dispatch({
      type: "REMOVE_EMPLOYEE",
      payload: id
    });
  }

  function addEmployee(employees) {
    dispatch({
      type: "ADD_EMPLOYEES",
      payload: employees
    });
  }

  function editEmployee(employees) {
    dispatch({
      type: "EDIT_EMPLOYEE",
      payload: employees
    });
  }

  return (
    <GlobalContext.Provider
      value={{
        employees: state.employees,
        removeEmployee,
        addEmployee,
        editEmployee
      }}
    >
      {children}
    </GlobalContext.Provider>
  );
};

We added some functionality to dispatch an action which goes into our reducer file to switch upon the case that corresponds to each action.

我们添加了一些功能来调度一个动作,该动作进入我们的reducer文件,以切换与每个动作相对应的大小写。

We also defined the initial state of our employee array with hard-coded values inside the object. Along with the dispatch type we will also add what payload it receives. Let’s move on to our AppReducer file and write some switch cases for CRUD functionality, 🤓 which looks like this:

我们还使用对象内部的硬编码值定义了雇员数组的初始状态。 除了调度类型外,我们还将添加它接收的有效负载。 让我们继续我们的AppReducer文件,并为CRUD功能编写一些切换案例,如下所示:

export default (state, action) => {
  switch (action.type) {
    case "REMOVE_EMPLOYEE":
      return {
        ...state,
        employees: state.employees.filter(
          employee => employee.id !== action.payload
        )
      };
    case "ADD_EMPLOYEES":
      return {
        ...state,
        employees: [...state.employees, action.payload]
      };
    case "EDIT_EMPLOYEE":
      const updatedEmployee = action.payload;

      const updatedEmployees = state.employees.map(employee => {
        if (employee.id === updatedEmployee.id) {
          return updatedEmployee;
        }
        return employee;
      });

      return {
        ...state,
        employees: updatedEmployees
      };
    default:
      return state;
  }
};

In our AppReducer.js file, we added our switch case and wrote some functionality for each case and returned employee state inside respective functions. We’ll move ahead with our AddEmployee component and write an onSubmit handler which will push the filled values of our form field into the state.

在我们的AppReducer.js文件中,我们添加了切换用例,并为每种用例编写了一些功能,并在各自的函数内返回了员工状态。 我们将继续使用AddEmployee组件,并编写一个onSubmit处理程序,该处理程序会将表单字段的填充值推入状态。

Below is how our code looks like:

下面是我们的代码的样子:

import React, { Fragment, useState, useContext } from "react";
import { GlobalContext } from "../context/GlobalState";
import { useHistory } from "react-router-dom";
import { Link } from "react-router-dom";

export const AddEmployee = () => {
  const [name, setName] = useState("");
  const [location, setLocation] = useState("");
  const [designation, setDesignation] = useState("");
  const { addEmployee, employees } = useContext(GlobalContext);
  let history = useHistory();

  const onSubmit = e => {
    e.preventDefault();
    const newEmployee = {
      id: employees.length + 1,
      name,
      location,
      designation
    };
    addEmployee(newEmployee);
    history.push("/");
  };

  return (
    <Fragment>
      <div className="w-full max-w-sm container mt-20 mx-auto">
        <form onSubmit={onSubmit}>
          <div className="w-full mb-5">
            <label
              className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
              htmlFor="name"
            >
              Name of employee
            </label>
            <input
              className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:text-gray-600"
              value={name}
              onChange={e => setName(e.target.value)}
              type="text"
              placeholder="Enter name"
            />
          </div>
          <div className="w-full  mb-5">
            <label
              className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
              htmlFor="location"
            >
              Location
            </label>
            <input
              className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:text-gray-600 focus:shadow-outline"
              value={location}
              onChange={e => setLocation(e.target.value)}
              type="text"
              placeholder="Enter location"
            />
          </div>
          <div className="w-full  mb-5">
            <label
              className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
              htmlFor="designation"
            >
              Designation
            </label>
            <input
              className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:text-gray-600"
              value={designation}
              onChange={e => setDesignation(e.target.value)}
              type="text"
              placeholder="Enter designation"
            />
          </div>
          <div className="flex items-center justify-between">
            <button className="mt-5 bg-green-400 w-full hover:bg-green-500 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
              Add Employee
            </button>
          </div>
          <div className="text-center mt-4 text-gray-500">
            <Link to="/">Cancel</Link>
          </div>
        </form>
      </div>
    </Fragment>
  );
};

Here setName, setLocation and setDesignation will access the current value we typed inside our form fields and wrap it in a new constant, newEmployee, with unique id adding one to the total length. We’ll add a parameter to our GlobalContext we imported and newEmployees as out parameter as it accepts employees as a payload inside our GlobalState. Finally we’ll change our route to our main screen where we’ll be able to see the newly added employees.

在这里, setNamesetLocationsetDesignation将访问在表单字段中键入的当前值,并将其包装在新的常量newEmployee ,其唯一ID将总长度加一。 我们将在导入的GlobalContext添加一个参数,并在outEmployee中将newEmployees添加为out参数,因为它在我们的GlobalState内部将雇员作为有效载荷接受。 最后,我们将路线更改到主屏幕,在此我们将能够看到新添加的员工。

We’ll go into our EditEmployee component and write some functionality for editing the existing objects from the state. If you have noticed we added:

我们将进入EditEmployee组件,并编写一些功能来从状态中编辑现有对象。 如果您注意到我们添加了:

path=“/edit/:id”

路径=“ / edit /:id”

To our App.js file where we shall route to the route parameter. Lets take a look at following code

到我们的App.js文件中,我们将在其中路由到route参数。 让我们看一下下面的代码

import React, { Fragment, useState, useContext, useEffect } from "react";
import { GlobalContext } from "../context/GlobalState";
import { useHistory, Link } from "react-router-dom";

export const Editemployee = route => {
  let history = useHistory();
  const { employees, editEmployee } = useContext(GlobalContext);
  const [selectedUser, setSeletedUser] = useState({
    id: null,
    name: "",
    designation: "",
    location: ""
  });
  const currentUserId = route.match.params.id;

  useEffect(() => {
    const employeeId = currentUserId;
    const selectedUser = employees.find(emp => emp.id === parseInt(employeeId));
    setSeletedUser(selectedUser);
  }, []);

  const onSubmit = e => {
    e.preventDefault();
    editEmployee(selectedUser);
    history.push("/");
  };

  const handleOnChange = (userKey, value) =>
    setSeletedUser({ ...selectedUser, [userKey]: value });

  if (!selectedUser || !selectedUser.id) {
    alert("Id dont match !");
  }

  return (
    <Fragment>
      <div className="w-full max-w-sm container mt-20 mx-auto">
        <form onSubmit={onSubmit}>
          <div className="w-full mb-5">
            <label
              className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
              htmlFor="name"
            >
              Name of employee
            </label>
            <input
              className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:text-gray-600 focus:shadow-outline"
              value={selectedUser.name}
              onChange={e => handleOnChange("name", e.target.value)}
              type="text"
              placeholder="Enter name"
            />
          </div>
          <div className="w-full  mb-5">
            <label
              className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
              htmlFor="location"
            >
              Location
            </label>
            <input
              className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:text-gray-600 focus:shadow-outline"
              value={selectedUser.location}
              onChange={e => handleOnChange("location", e.target.value)}
              type="text"
              placeholder="Enter location"
            />
          </div>
          <div className="w-full  mb-5">
            <label
              className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
              htmlFor="designation"
            >
              Designation
            </label>
            <input
              className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:text-gray-600 focus:shadow-outline"
              value={selectedUser.designation}
              onChange={e => handleOnChange("designation", e.target.value)}
              type="text"
              placeholder="Enter designation"
            />
          </div>
          <div className="flex items-center justify-between">
            <button className="block mt-5 bg-green-400 w-full hover:bg-green-500 text-white font-bold py-2 px-4 rounded focus:text-gray-600 focus:shadow-outline">
              Edit Employee
            </button>
          </div>
          <div className="text-center mt-4 text-gray-500">
            <Link to="/">Cancel</Link>
          </div>
        </form>
      </div>
    </Fragment>
  );
};

Here we used the useEffect hook, which is invoked when the component is mounted. 💣 Inside this hook we’ll know the current route parameter and find the same parameter to our employees object from the state. We then created the setSelectedUser function and passed selectedUser as its parameter. We then observe for onChange events on our form fields where we pass on userKey and value as two parameters. We spread selectedUser and set userKey as value from the input fields.

在这里,我们使用了useEffect钩子 ,该钩子在安装组件时被调用。 hook在此挂钩中,我们将知道当前的路线参数,并从该状态为我们的雇员对象找到相同的参数。 然后,我们创建了setSelectedUser函数并传递了selectedUser作为其参数。 然后,我们在表单字段上观察onChange事件,在其中我们将userKey和value传递为两个参数。 我们在输入字段中传播selectedUser并将userKey设置为值。

Finally invoking the onSubmit event works just fine. 🌈🌈 Here we have successfully created our CRUD application using the Context API and hooks.

最后,调用onSubmit事件就可以了。 🌈🌈在这里,我们已经使用Context API和钩子成功创建了CRUD应用程序。

You can also find the code in a GitHub repository here.

您还可以在GitHub存储库中找到代码。

Happy Coding!

编码愉快!

翻译自: https://www.digitalocean.com/community/tutorials/react-crud-context-hooks

react hooks使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值