英文:
React DOM Exception error after removing dom child item
问题
主要问题: 当我删除任务(从ul
中删除li
项目)后,个人状态发生变化(注意我是React的新手):
Uncaught DOMException:在<li>
组件中发生了以下错误:
在li
中
在ul
中
在section
中
在div
中
在Tasks
(http://localhost:3000/static/js/bundle.js:796:5)
在div
中
在MyComponent
(http://localhost:3000/static/js/bundle.js:195:86)
在RenderedRoute
(http://localhost:3000/static/js/bundle.js:40022:5)
在Routes
(http://localhost:3000/static/js/bundle.js:40488:5)
在Router
(http://localhost:3000/static/js/bundle.js:40426:15)
在BrowserRouter
(http://localhost:3000/static/js/bundle.js:38680:5)
在App
在div
在App
考虑添加错误边界到你的树以自定义错误处理行为。访问https://reactjs.org/link/error-boundaries 以了解更多有关错误边界的信息。
这是代码示例:
const Tasks = ({ page, personal }) => {
const workTasks = useMemo(() => {
const data = JSON.parse(localStorage.getItem("Tasks"));
return data.filter((item) => item.properties.personal !== true);
}, []);
const personalTasks = useMemo(() => {
const data = JSON.parse(localStorage.getItem("Tasks"));
return data.filter((item) => item.properties.personal === true);
}, []);
}
上面的代码从父组件中获取当前页面(home)和个人(boolean)。个人状态用于添加个人或工作任务。然后,根据个人状态渲染工作或个人任务列表。
在添加任务后,尝试构建removeFunction
来删除任务项目(li
),可以使用filter()
或直接使用任务名称的id
删除li
节点子元素。
用于从ul
中删除li
元素的代码:
function removeItem(tasks, index) {
const li = document.getElementsByClassName(tasks.task).item(0);
if (li) li.parentNode.removeChild(li);
deleteTask(tasks); // 这会从本地存储中删除项目
}
用于渲染ul
(任务)的代码:
<section>
{personal === true && page === 'home' ? (
<ul id='Personaltasks'>
{personalTasks.map((tasks, index) => (
<li key={index} className={tasks.task}>
<InlineEdit properties={tasks} value={tasks.task} setValue={setTask} />
<img href='/' alt='✖' onClick={() => removeItem(tasks, index)} />
</li>
))}
</ul>
) : (
<ul id='Worktasks'>
{workTasks.map((tasks, index) => (
<li key={index} className={tasks.task}>
<InlineEdit properties={tasks} value={tasks.task} setValue={setTask} />
<img href='/' alt='✖' onClick={() => removeItem(tasks, index)} />
</li>
))}
</ul>
)}
</section>
英文:
Main Issue: When The personal state changes right after I remove task's (removing li item from ul) client encounters (note I am new to react) :
Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
The above error occurred in the <li> component:
at li
at ul
at section
at div
at Tasks (http://localhost:3000/static/js/bundle.js:796:5)
at div
at MyComponent (http://localhost:3000/static/js/bundle.js:195:86)
at RenderedRoute (http://localhost:3000/static/js/bundle.js:40022:5)
at Routes (http://localhost:3000/static/js/bundle.js:40488:5)
at Router (http://localhost:3000/static/js/bundle.js:40426:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:38680:5)
at App
at div
at App
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
This is the code example
const Tasks = ({ page, personal }) => { const workTasks = useMemo(()=>{
const data = JSON.parse(localStorage.getItem("Tasks"))
return data.filter((item)=> item.properties.personal !== true)},[])
const personalTasks = useMemo(()=>{
const data = JSON.parse(localStorage.getItem("Tasks"))
return data.filter((item)=> item.properties.personal === true)},[])
The code above takes the current page(home) and personal(boolean) from parent component.
The personal state is used to add personal or work tasks. Then we render a list of either work or personal as our personal state.
after adding I attempt to build a removeFucntion to remove task item (li) either using filter() or removing the li node child directly with its id which is the tasks name.
code for removing the li element from the ul:
function removeItem(tasks,index) {
const li = document.getElementsByClassName(tasks.task).item(0)
if(li) li.parentNode.removeChild(li)
deleteTask(tasks) //this removes the the item from localStorage
}
code for rendering ul(tasks):
<section>
{personal === true && page === 'home' ?
<ul id='Personaltasks'>
{ personalTasks.map((tasks, index) =>
( <li key={index} className={tasks.task} > <InlineEdit properties={tasks} value={tasks.task} setValue={setTask} />
<img href='/' alt={'&#10006;'} onClick={()=> removeItem(tasks,index)} />
</li>))
}
</ul>
:
<ul id='Worktasks'>
{ workTasks.map((tasks, index) =>
( <li key={index} className={tasks.task} > <InlineEdit properties={tasks} value={tasks.task} setValue={setTask} />
<img href='/' alt={'&#10006;'} onClick={()=> removeItem(tasks,index)} />
</li>))
}
</ul>}
</section>
答案1
得分: 1
-
永远不要在React中引用DOM或其函数(例如getElementsByClassName,removeElement等)。这是React应该为您执行的行为。您的JSX会在您的变量更新时更新。
-
我不确定您对UseMemo()的使用是否有任何用处。我已经更新了下面的行为,使其更易读。
-
或许可以使用
name
或一个唯一标识的属性作为removeTask函数的参数。 -
除了在
<button />
标记内部,永远不要使用onClick()。不过好消息是,您可以将<img />
标记包装在一个按钮中。在此处可以了解有关使用onClick的注意事项。
这里是我建立的一个示例解决方案:
import { useState, useMemo } from 'react';
export const MyComponent = ({ personal = true, page = 'home' }) => {
const [tasks, setTasks] = useState(JSON.parse(localStorage.getItem('Tasks')) || []);
const personalTasks = useMemo(() => tasks.filter(item => item.properties.personal), [tasks]);
const workTasks = useMemo(() => tasks.filter(item => !item.properties.personal), [tasks]);
function removeTask(name) {
setTasks(tasks.filter(item => item.name !== name));
}
return (
<section>
{personal && page === 'home' ? (
<ul id="PersonalTasks">
{personalTasks.map((task, index) => (
<li key={index} className={task.name}>
{' '}
<InlineEdit properties={task} value={task.name} setValue={() => {}} />
<button type="button" onClick={() => removeTask(task.name)}>
<img src="/" alt="✖" />
</button>
</li>
))}
</ul>
) : (
<ul id="WorkTasks">
{workTasks.map((task, index) => (
<li key={index} className={task.task}>
{' '}
<InlineEdit properties={task} value={task.name} setValue={() => {}} />
<button type="button" onClick={() => removeTask(task.name)}>
<img src="/" alt="✖" />
</button>
</li>
))}
</ul>
)}
</section>
);
};
如果这对您有帮助,请告诉我!
1: https://stackoverflow.com/a/50736536/9108451
英文:
I recommend a few things
- Never reference the DOM or it's functions in React (getElementsByClassName, removeElement, etc). That's the behaviour React should be doing for you. Your JSX updates when your variables update.
- I'm not sure your uses of UseMemo() are doing anything useful. I've updated this behaviour below to be a bit more readable.
- Maybe use
name
or a uniquely identifying property as a param for your removeTask function. - Never use onClick() except for inside a
<button />
tag. Good news though: you can wrap your<img />
tags in a button. See here why you should be careful with onClick.
Here's an example solution I built:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { useState, useMemo } from 'react';
export const MyComponent = ({ personal = true, page = 'home' }) => {
const [tasks, setTasks] = useState(JSON.parse(localStorage.getItem('Tasks')) || []);
const personalTasks = useMemo(() => tasks.filter(item => item.properties.personal), [tasks]);
const workTasks = useMemo(() => tasks.filter(item => !item.properties.personal), [tasks]);
function removeTask(name) {
setTasks(tasks.filter(item => item.name !== name));
}
return (
<section>
{personal && page === 'home' ? (
<ul id="Personaltasks">
{personalTasks.map((task, index) => (
<li key={index} className={task.name}>
{' '}
<InlineEdit properties={task} value={task.name} setValue={() => {}} />
<button type="button" onClick={() => removeTask(task.name)}>
<img href="/" alt={'&#10006;'} />
</button>
</li>
))}
</ul>
) : (
<ul id="Worktasks">
{workTasks.map((task, index) => (
<li key={index} className={task.task}>
{' '}
<InlineEdit properties={task} value={task.name} setValue={() => {}} />
<button type="button" onClick={() => removeTask(task.name)}>
<img href="/" alt={'&#10006;'} />
</button>
</li>
))}
</ul>
)}
</section>
);
};
<!-- end snippet -->
Let me know if that's any good!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论