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
导入Route
和Switch
。 事先我们将用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 className
s that you’ll notice are Tailwind CSS utility classes and they help style our app.
现在,我们将继续在EmployeeList.js文件中打印雇员列表。 您将注意到的className
是Tailwind CSS实用程序类 ,它们有助于设计我们的应用程序。
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.
在上面的代码中,我们导入了GlobalState
和useContext ,它们是内置的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.
在这里, setName
, setLocation
和setDesignation
将访问在表单字段中键入的当前值,并将其包装在新的常量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使用