[Actual Combat] Onze. Développement de pages Kanban et de pages de groupes de tâches (3) —— Meilleures pratiques React17+React Hook+TS4, imitant les projets Jira au niveau de l'entreprise (25)


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 useDebouncefait 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 Drawerpour , lorsque Modalvous utilisez la limite useForm()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…formFormforceRender

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 useTasksModalde manière à ce que cliquer sur une carte de tâche s'ouvre pour TaskModall'é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 TaskModalapparaître, modifiez et confirmez pour voir la tâche modifiée (en utilisant la mise à jour optimiste, complètement insensible) :
insérer la description de l'image ici

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 Tasket TaskCardextrait 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 :

insérer la description de l'image ici

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. . .

Je suppose que tu aimes

Origine blog.csdn.net/qq_32682301/article/details/132420837
conseillé
Classement