Обновление пользовательского интерфейса после удаления элемента в React

#javascript #reactjs

Вопрос:

Я новичок в реагировании, и у меня проблема с тем, что мой пользовательский интерфейс не обновляется, как только я отправляю запрос на удаление в своем приложении React. Я пытался использовать a useEffect на своем deleteTaskHandler , но это нарушило мой код. Есть какие-нибудь идеи, как выполнить это обновление?

Это мой Task.js файл, который получает реквизиты из TaskList.js файла, а TaskList.js файл отправляет компонент в App.js :

 import React, { useState } from 'react';
import classes from './Task.module.css';

const Task = (props) => {
    const [isCompleted, setIsCompleted] = useState(props.isCompleted);
    const changeCompleteStatus = () => {
        setIsCompleted(!isCompleted);
    }


const deleteTaskHandler = async () => {
    try {
        const key = props.id
        const response = await fetch('http://localhost:5050/delete-task/'   key, {
            method: 'DELETE'
        });

        if (!response.ok) {
            throw new Error('Something went wrong!');
        };

        const data = await response.json();
        console.log(data);

    } catch (error) {
        console.log(error);
    }

};


const updateTaskHandler = async () => {
    const id = props.id
    const taskData = {
        id: id,
        content: props.content,
        isCompleted: !props.isCompleted,
        dateCreation: props.dateCreation,
    };

    try {
        const response = await fetch('http://localhost:5050/edit-task/'   id, {
            method: 'PATCH',
            body: JSON.stringify(taskData),
            headers: {
                'Content-Type': 'application/json'
            }
        });

        if (!response.ok) {
            throw new Error('Something went wrong!');
        };

        const data = await response.json();
        console.log(data);

    } catch (error) {
        console.log(error);
    }



};

let task;
if (props.isAllView) {
    task = <div >
        <input type="checkbox" onClick={updateTaskHandler} onChange={changeCompleteStatus} checked={isCompleted} />
        <h2>{props.content}</h2>
        <h3>{props.dateCreation}</h3>
        <button onClick={deleteTaskHandler}>X</button>
    </div>
} else {
    task = <div >
        <h2>{props.content}</h2>
        <h3>{props.dateCreation}</h3>
    </div>
}

return (
    <li>{task}</li>
);
};

export default Task;
 

Это TaskList.js :

 import React, { useState } from 'react';
import classes from './TaskList.module.css';
import Task from './Task';

const TaskList = (props) => {
    const [taskView, setTaskView] = useState('all');

    const getCompleteURL = () => {
        setTaskView('complete')
        props.onChangeTaskURL('http://localhost:5050/completed');
    };
    const getAllURL = () => {
        setTaskView('all')
        props.onChangeTaskURL('http://localhost:5050/');
    };
    const getPendingURL = () => {
        setTaskView('pending')
        props.onChangeTaskURL('http://localhost:5050/pending');
    };

    let taskList;

    if (taskView != 'all') {
        taskList = props.taskData.map((task) => (
            <Task
                key={task.id}
                content={task.content}
                dateCreation={task.dateCreation}
                isCompleted={task.isCompleted}
                isAllView={false}
            />
        ));
    } else {
        taskList = props.taskData.map((task) => (
            <Task
                key={task.id}
                id={task.id}
                content={task.content}
                dateCreation={task.dateCreation}
                isCompleted={task.isCompleted}
                isAllView={true}
            />
        ));
    }

    return (
        <div>
            <ul >
                {taskList}
            </ul>
            <button onClick={getCompleteURL}>Completed</button>
            <button onClick={getAllURL}>All</button>
            <button onClick={getPendingURL}>Pending</button>
        </div>
    );
};

export default TaskList;
 

Это App.js :

 import React, { useState, useEffect, useCallback } from 'react';
import './App.css';
import TaskList from './components/Tasks/TaskList';
import NewTask from './components/NewTask/NewTask';

function App() {
  const [tasks, setTasks] = useState([]);
  const [taskURL, setTaskURL] = useState('http://localhost:5050/');

  const fetchTasksHandler = useCallback(async (url) => {
    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error('Something went wrong!');
      }

      const data = await response.json();

      const loadedTasks = [];

      for (const key in data) {
        loadedTasks.push({
          id: data[key]._id,
          content: data[key].content,
          isCompleted: data[key].isCompleted,
          dateCreation: data[key].dateCreation
        });
      }
      console.log(loadedTasks)
      setTasks(loadedTasks);
    } catch (error) {
      // throw new Error('Something went wrong!');
      console.log(error)
    }

  }, []);

  useEffect(() => {
    fetchTasksHandler(taskURL);
  }, [fetchTasksHandler, taskURL]);

  const changeTaskURL = url => {
    console.log(url)
    setTaskURL(url);
  };

  return (
    <React.Fragment>
      <TaskList taskData={tasks} onChangeTaskURL={changeTaskURL}></TaskList>
      <NewTask></NewTask>
    </React.Fragment>
  );
}

export default App;
 

Комментарии:

1. Вам нужно использовать setState() и начать заполнять пользовательский интерфейс из состояния. reactjs.org/docs/state-and-lifecycle.html

Ответ №1:

Извлеките deleteTaskHandler и updateTaskHandler в свой App.js и передайте их в TaskList => > Task . В обоих методах при успешной операции обновите массив tasks состояний (для удаления — отфильтруйте удаленную задачу, для обновления — замените старую задачу обновленной). Таким образом, Task компонент вызовет соответствующий обработчик, который обновит родительское tasks состояние, которое, в свою очередь, перейдет в TaskList и Task , и все будет обновляться автоматически.

Вот пример. Рассматривайте это скорее как псевдокод, так как вам придется изменить некоторые части, чтобы соответствующим образом обработать ваше дело.

Ваш Task.js :

 import React, { useState } from 'react';
import classes from './Task.module.css';

const Task = (props) => {
    const {
        updateTaskHandler, 
        deleteTaskHandler  
    } = props;

    const [isCompleted, setIsCompleted] = useState(props.isCompleted);

    const changeCompleteStatus = () => {
        setIsCompleted(!isCompleted);
    }

    const updateHandler = () => {
        const taskData = {
            id: id,
            content: props.content,
            isCompleted: !props.isCompleted,
            dateCreation: props.dateCreation,
        };

        updateTaskHandler(props.id, taskData);
    };

    const deleteHandler = () => {
        deleteTaskHandler(props.id);
    };

    let task;

    if (props.isAllView) {
        task = <div >
            <input type="checkbox" onClick={updateHandler} onChange={changeCompleteStatus} checked={isCompleted} />
            <h2>{props.content}</h2>
            <h3>{props.dateCreation}</h3>
            <button onClick={deleteHandler}>X</button>
        </div>
    } else {
        task = <div >
            <h2>{props.content}</h2>
            <h3>{props.dateCreation}</h3>
        </div>
    }

    return (
        <li>{task}</li>
    );
};

export default Task;
 

TaskList.js :

 import React, { useState } from 'react';
import classes from './TaskList.module.css';
import Task from './Task';

const TaskList = (props) => {
    const [taskView, setTaskView] = useState('all');

    const getCompleteURL = () => {
        setTaskView('complete')
        props.onChangeTaskURL('http://localhost:5050/completed');
    };

    const getAllURL = () => {
        setTaskView('all')
        props.onChangeTaskURL('http://localhost:5050/');
    };

    const getPendingURL = () => {
        setTaskView('pending')
        props.onChangeTaskURL('http://localhost:5050/pending');
    };

    let taskList;

    if (taskView != 'all') {
        taskList = props.taskData.map((task) => (
            <Task
                key={task.id}
                content={task.content}
                dateCreation={task.dateCreation}
                isCompleted={task.isCompleted}
                isAllView={false}
                updateTaskHandler={props.updateTaskHandler}
                deleteTaskHandler={props.deleteTaskHandler}
            />
        ));
    } else {
        taskList = props.taskData.map((task) => (
            <Task
                key={task.id}
                id={task.id}
                content={task.content}
                dateCreation={task.dateCreation}
                isCompleted={task.isCompleted}
                isAllView={true}
                updateTaskHandler={props.updateTaskHandler}
                deleteTaskHandler={props.deleteTaskHandler}
            />
        ));
    }

    return (
        <div>
            <ul >
                {taskList}
            </ul>
            <button onClick={getCompleteURL}>Completed</button>
            <button onClick={getAllURL}>All</button>
            <button onClick={getPendingURL}>Pending</button>
        </div>
    );
};

export default TaskList;
 

И App.js :

 import React, { useState, useEffect, useCallback } from 'react';
import './App.css';
import TaskList from './components/Tasks/TaskList';
import NewTask from './components/NewTask/NewTask';

function App() {
    const [tasks, setTasks] = useState([]);
    const [taskURL, setTaskURL] = useState('http://localhost:5050/');

    const fetchTasksHandler = useCallback(async (url) => {
        try {
            const response = await fetch(url);

            if (!response.ok) {
                throw new Error('Something went wrong!');
            }

            const data = await response.json();

            const loadedTasks = [];

            for (const key in data) {
                loadedTasks.push({
                    id: data[key]._id,
                    content: data[key].content,
                    isCompleted: data[key].isCompleted,
                    dateCreation: data[key].dateCreation
                });
            }

            setTasks(loadedTasks);
        } 
        catch (error) {
            // throw new Error('Something went wrong!');
            console.log(error)
        }

    }, []);

    const deleteTaskHandler = async (taskID) => {
        try {
            const response = await fetch(`http://localhost:5050/delete-task/${taskID}`, {
                method: 'DELETE'
            });

            if (!response.ok) {
                throw new Error('Something went wrong!');
            };

            const data = await response.json();

            setTasks(tasks => {
                return tasks.filter(task => task.id !== taskID)
            });
        }
        catch (error) {
            console.log(error);
        }
    };

    const updateTaskHandler = async (taskID, taskData) => {
        const id = props.id
        const taskData = {
            id: id,
            content: props.content,
            isCompleted: !props.isCompleted,
            dateCreation: props.dateCreation,
        };

        try {
            const response = await fetch(`http://localhost:5050/edit-task/${taskID}`, {
                method: 'PATCH',
                body: JSON.stringify(taskData),
                headers: {
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                throw new Error('Something went wrong!');
            };

            const data = await response.json();

            setTasks(tasks => {
                return tasks.map(task => {
                    if (task.id !== taskID) {
                        return task;
                    }
                    else {
                        return data; // The updated task
                    }
                })
            });
        }
        catch (error) {
            console.log(error);
        }
    };

    useEffect(() => {
        fetchTasksHandler(taskURL);
    }, [fetchTasksHandler, taskURL]);

    const changeTaskURL = url => {
        console.log(url)
        setTaskURL(url);
    };

    return (
        <React.Fragment>
            <TaskList 
                taskData={tasks} 
                onChangeTaskURL={changeTaskURL}
                deleteTaskHandler={deleteTaskHandler}
                updateTaskHandler={updateTaskHandler}
            />
            <NewTask />
        </React.Fragment>
    );
}

export default App;
 

Ответ №2:

Вы должны передать обратный вызов, который удаляет задачу и передает ее в качестве поддержки Task компоненту.

В вашем App.js:

 function deleteTask(id) {
  setTasks(loadedTasks.filter(x => x.id !== id);
}

// ...

<TaskList taskData={tasks} onDelete={deleteTask}></TaskList>
 

В вашем TaskList.js:

 tasks.map(task => <Task id={task.id} key={task.id} onDelete={props.deleteTask}></Task>)
 

В Task.js звоните props.deleteTask(props.id) , когда вам нужно.

Обратите внимание, что прохождение опоры через два или более компонентов называется «сверлением опоры», и его следует избегать (сохраняя состояние в TaskList.js например).

Ответ №3:

Вы можете использовать

 window.location.reload();
 

чтобы обновить страницу или

 this.setState({});
 

чтобы обновить компонент или

   const [value,setValue] = useState();

  const refresh = ()=>{
   setValue({});
  }
 

чтобы обновить компонент с помощью крючков
, я надеюсь, что вы нашли этот ответ полезным

Комментарии:

1. window.location.reload(); это плохая практика.

2. @Aswath Ну, а почему window.location.reload() это плохая практика?

3. Одностраничное приложение предназначено для обновления страницы, а не для ее перезагрузки при каждом изменении, так что это действительно плохая практика.