Annuaire d'articles
-
- 1. Lancement du projet : initialisation et configuration du projet
- 2. Application React et Hook : implémenter la liste de projets
- 3. Application TS : JS God Assist – Type fort
- 4. JWT, authentification des utilisateurs et requête asynchrone
- 5. CSS est en fait très simple : ajoutez des styles avec CSS-in-JS
- 6. Optimisation de l'expérience utilisateur - chargement et gestion des états d'erreur
- 7. Gestion des hooks, du routage et de l'état des URL
- 8. Sélecteur d'utilisateur et fonction d'édition d'éléments
- 9. Gestion approfondie de l'état React et mécanisme Redux
- 10. Utilisez React-Query pour obtenir des données et gérer le cache
- 11. Développement de pages Kanban et de pages de groupes de tâches
Source du contenu d'apprentissage : React + React Hook + TS Best Practice - MOOC
Par rapport au tutoriel original, j'ai utilisé la dernière version au début de mon étude (2023.03) :
article | Version |
---|---|
réagir et réagir-dom | ^18.2.0 |
réagir-routeur et réaction-routeur-dom | ^6.11.2 |
et | ^4.24.8 |
@commitlint/cli & @commitlint/config-conventional | ^17.4.4 |
eslint-config-plus joli | ^8.6.0 |
rauque | ^8.0.3 |
pelucheux | ^13.1.2 |
plus jolie | 2.8.4 |
serveur json | 0.17.2 |
sans craco | ^2.0.0 |
@craco/craco | ^7.1.0 |
qs | ^6.11.0 |
jourjs | ^1.11.7 |
casque de réaction | ^6.1.0 |
@types/react-casque | ^6.1.6 |
requête de réaction | ^6.1.0 |
@welldone-software/pourquoi-avez-vous-rendu | ^7.0.1 |
@emotion/réagir & @emotion/styled | ^11.10.6 |
La configuration spécifique, le fonctionnement et le contenu seront différents, et la « fosse » sera également différente. . .
1. Lancement du projet : initialisation et configuration du projet
2. Application React et Hook : implémenter la liste de projets
3. Application TS : JS God Assist – Type fort
4. JWT, authentification des utilisateurs et requête asynchrone
5. CSS est en fait très simple : ajoutez des styles avec CSS-in-JS
6. Optimisation de l'expérience utilisateur - chargement et gestion des états d'erreur
7. Gestion des hooks, du routage et de l'état des URL
8. Sélecteur d'utilisateur et fonction d'édition d'éléments
9. Gestion approfondie de l'état React et mécanisme Redux
10. Utilisez React-Query pour obtenir des données et gérer le cache
11. Développement de pages Kanban et de pages de groupes de tâches
1~3
4~6
7. Modifier la fonction de tâche
Ensuite, créez un composant pour les tâches d'édition :
Si vous êtes prêt à appeler l'interface de tâche d'édition et à obtenir les détails de la tâche Hook
, modifiezsrc\utils\task.ts
:
...
import {
useAddConfig, useEditConfig } from "./use-optimistic-options";
export const useEditTask = (queryKey: QueryKey) => {
const client = useHttp();
return useMutation(
(params: Partial<Task>) =>
client(`tasks/${
params.id}`, {
method: "PATCH",
data: params,
}),
useEditConfig(queryKey)
);
};
export const useTask = (id?: number) => {
const client = useHttp();
return useQuery<Task>(["task", id], () => client(`tasks/${
id}`), {
enabled: Boolean(id),
});
};
EDIT src\screens\ViewBoard\utils.ts
(ajouté useTasksModal
) :
...
// import { useDebounce } from "utils";
import {
useTask } from "utils/task";
...
export const useTasksSearchParams = () => {
const [param] = useUrlQueryParam([
"name",
"typeId",
"processorId",
"tagId",
]);
const projectId = useProjectIdInUrl();
// const debouncedName = useDebounce(param.name)
return useMemo(
() => ({
projectId,
typeId: Number(param.typeId) || undefined,
processorId: Number(param.processorId) || undefined,
tagId: Number(param.tagId) || undefined,
// name: debouncedName,
name: param.name,
}),
// [projectId, param, debouncedName]
[projectId, param]
);
};
...
export const useTasksModal = () => {
const [{
editingTaskId }, setEditingTaskId] = useUrlQueryParam(['editingTaskId'])
const {
data: editingTask, isLoading } = useTask(Number(editingTaskId))
const startEdit = useCallback((id: number) => {
setEditingTaskId({
editingTaskId: id})
}, [setEditingTaskId])
const close = useCallback(() => {
setEditingTaskId({
editingTaskId: ''})
}, [setEditingTaskId])
return {
editingTaskId,
editingTask,
startEdit,
close,
isLoading
}
}
L'utilisation de dans la vidéo
useDebounce
fait démarrer la recherche après l'arrêt complet de la saisie, évitant ainsi le gaspillage de ressources système causé par des recherches fréquentes pendant le processus de saisie et affectant l'expérience utilisateur. Une fois que le blogueur a apporté de telles modifications, la méthode de saisie chinoise ne peut pas être utilisé normalement. . . Suivi
Nouveau composant :src\screens\ViewBoard\components\taskModal.tsx
:
import {
useForm } from "antd/lib/form/Form"
import {
useTasksModal, useTasksQueryKey } from "../utils"
import {
useEditTask } from "utils/task"
import {
useEffect } from "react"
import {
Form, Input, Modal } from "antd"
import {
UserSelect } from "components/user-select"
import {
TaskTypeSelect } from "components/task-type-select"
const layout = {
labelCol: {
span: 8},
wrapperCol: {
span: 16}
}
export const TaskModal = () => {
const [form] = useForm()
const {
editingTaskId, editingTask, close } = useTasksModal()
const {
mutateAsync: editTask, isLoading: editLoading } = useEditTask(useTasksQueryKey())
const onCancel = () => {
close()
form.resetFields()
}
const onOk = async () => {
await editTask({
...editingTask, ...form.getFieldsValue()})
close()
}
useEffect(() => {
form.setFieldsValue(editingTask)
}, [form, editingTask])
return <Modal
forceRender={
true}
onCancel={
onCancel}
onOk={
onOk}
okText={
"确认"}
cancelText={
"取消"}
confirmLoading={
editLoading}
title={
"编辑任务"}
open={
!!editingTaskId}
>
<Form {
...layout} initialValues={
editingTask} form={
form}>
<Form.Item
label={
"任务名"}
name={
"name"}
rules={
[{
required: true, message: "请输入任务名" }]}
>
<Input />
</Form.Item>
<Form.Item label={
"经办人"} name={
"processorId"}>
<UserSelect defaultOptionName={
"经办人"} />
</Form.Item>
<Form.Item label={
"类型"} name={
"typeId"}>
<TaskTypeSelect />
</Form.Item>
</Form>
</Modal>
}
Remarque : Comme
Drawer
pour , lorsqueModal
vous utilisez la limiteuseForm()
extraite dans le composant , vous devez ajouter la propriété, sinon lorsque la page est ouverte, si la liaison n'est pas disponible, une erreur sera signalée . useForm' n'est connecté à aucun élément Form . Oublier…form
Form
forceRender
EDIT : src\screens\ViewBoard\index.tsx
(introduitTaskModal
):
...
import {
TaskModal } from "./components/taskModal";
export const ViewBoard = () => {
...
return (
<ViewContainer>
...
<TaskModal/>
</ViewContainer>
);
};
...
EDIT : src\screens\ViewBoard\components\ViewboardCloumn.tsx
(introduit useTasksModal
de manière à ce que cliquer sur une carte de tâche s'ouvre pour TaskModal
l'édition) :
...
import {
useTasksModal, useTasksSearchParams } from "../utils";
...
export const ViewboardColumn = ({
viewboard }: {
viewboard: Viewboard }) => {
...
const {
startEdit } = useTasksModal()
return (
<Container>
...
<TasksContainer>
{
tasks?.map((task) => (
<Card onClick={
() => startEdit(task.id)} style={
{
marginBottom: "0.5rem", cursor: 'pointer' }} key={
task.id}>
...
</Card>
))}
...
</TasksContainer>
</Container>
);
};
...
Visualisez les fonctions et les effets, cliquez sur la fiche de tâche pour TaskModal
apparaître, modifiez et confirmez pour voir la tâche modifiée (en utilisant la mise à jour optimiste, complètement insensible) :
8. Kanban et fonction de suppression de tâches
Ensuite, implémentez une petite fonction pour mettre en évidence les mots-clés dans les résultats de recherche
Nouveau src\screens\ViewBoard\components\mark.tsx
:
export const Mark = ({
name, keyword}: {
name: string, keyword: string}) => {
if(!keyword) {
return <>{
name}</>
}
const arr = name.split(keyword)
return <>
{
arr.map((str, index) => <span key={
index}>
{
str}
{
index === arr.length -1 ? null : <span style={
{
color: '#257AFD' }}>{
keyword}</span>
}
</span>)
}
</>
}
EDIT src\screens\ViewBoard\components\ViewboardCloumn.tsx
(introduit Task
et TaskCard
extrait séparément) :
...
import {
Task } from "types/Task";
import {
Mark } from "./mark";
...
const TaskCard = ({
task}: {
task: Task}) => {
const {
startEdit } = useTasksModal();
const {
name: keyword } = useTasksSearchParams()
return <Card
onClick={
() => startEdit(task.id)}
style={
{
marginBottom: "0.5rem", cursor: "pointer" }}
key={
task.id}
>
<p>
<Mark keyword={
keyword} name={
task.name}/>
</p>
<TaskTypeIcon id={
task.id} />
</Card>
}
export const ViewboardColumn = ({
viewboard }: {
viewboard: Viewboard }) => {
const {
data: allTasks } = useTasks(useTasksSearchParams());
const tasks = allTasks?.filter((task) => task.kanbanId === viewboard.id);
return (
<Container>
<h3>{
viewboard.name}</h3>
<TasksContainer>
{
tasks?.map((task) => <TaskCard task={
task}/>)}
<CreateTask kanbanId={
viewboard.id} />
</TasksContainer>
</Container>
);
};
...
Visualisez l'effet :
Commençons par développer la fonction de suppression
EDIT src\utils\viewboard.ts
(créé et exporté useDeleteViewBoard
) :
...
export const useDeleteViewBoard = (queryKey: QueryKey) => {
const client = useHttp();
return useMutation(
(id?: number) =>
client(`kanbans/${
id}`, {
method: "DELETE",
}),
useDeleteConfig(queryKey)
);
};
MODIFIERsrc\screens\ViewBoard\components\ViewboardCloumn.tsx
:
...
import {
Button, Card, Dropdown, MenuProps, Modal, Row } from "antd";
import {
useDeleteViewBoard } from "utils/viewboard";
...
export const ViewboardColumn = ({
viewboard }: {
viewboard: Viewboard }) => {
const {
data: allTasks } = useTasks(useTasksSearchParams());
const tasks = allTasks?.filter((task) => task.kanbanId === viewboard.id);
return (
<Container>
<Row>
<h3>{
viewboard.name}</h3>
<More viewboard={
viewboard}/>
</Row>
<TasksContainer>
{
tasks?.map((task) => <TaskCard task={
task}/>)}
<CreateTask kanbanId={
viewboard.id} />
</TasksContainer>
</Container>
);
};
const More = ({
viewboard }: {
viewboard: Viewboard }) => {
const {
mutateAsync: deleteViewBoard} = useDeleteViewBoard(useViewBoardQueryKey())
const startDelete = () => {
Modal.confirm({
okText: '确定',
cancelText: '取消',
title: '确定删除看板吗?',
onOk() {
deleteViewBoard(viewboard.id)
}
})
}
const items: MenuProps["items"] = [
{
key: 1,
label: "删除",
onClick: startDelete,
},
];
return <Dropdown menu={
{
items }}>
<Button type="link" onClick={
(e) => e.preventDefault()}>
...
</Button>
</Dropdown>
}
...
Test pour supprimer le Kanban, le fonctionnement est normal
Ce qui suit est la fonction de suppression de tâche
EDIT src\utils\task.ts
(créé et exporté useDeleteTask
) :
...
export const useDeleteTask = (queryKey: QueryKey) => {
const client = useHttp();
return useMutation(
(id?: number) =>
client(`tasks/${
id}`, {
method: "DELETE",
}),
useDeleteConfig(queryKey)
);
};
MODIFIERsrc\screens\ViewBoard\components\taskModal.tsx
:
...
import {
useDeleteTask, useEditTask } from "utils/task";
export const TaskModal = () => {
...
const {
mutateAsync: deleteTask } = useDeleteTask(useTasksQueryKey());
...
const startDelete = () => {
close();
Modal.confirm({
okText: '确定',
cancelText: '取消',
title: '确定删除看板吗?',
onOk() {
deleteTask(Number(editingTaskId));
}
})
}
return (
<Modal {
...}>
<Form {
...}>
...
</Form>
<div style={
{
textAlign: 'right' }}>
<Button style={
{
fontSize: '14px'}} size="small" onClick={
startDelete}>删除</Button>
</div>
</Modal>
);
};
Test pour supprimer la tâche, la fonction est normale
Certaines notes de référence sont encore à l’état de projet, alors restez à l’écoute. . .