React DOM异常错误,在移除DOM子项后发生。

huangapple go评论79阅读模式
英文:

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='&#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>
英文:

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 &#39;removeChild&#39; on &#39;Node&#39;: The node to be removed is not a child of this node.

The above error occurred in the &lt;li&gt; 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 }) =&gt; { const workTasks = useMemo(()=&gt;{
const data = JSON.parse(localStorage.getItem(&quot;Tasks&quot;))
return data.filter((item)=&gt; item.properties.personal !== true)},[])

const personalTasks = useMemo(()=&gt;{
const data = JSON.parse(localStorage.getItem(&quot;Tasks&quot;))
return data.filter((item)=&gt; 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):

  &lt;section&gt;
    {personal === true &amp;&amp; page === &#39;home&#39; ?
     &lt;ul id=&#39;Personaltasks&#39;&gt;
      { personalTasks.map((tasks, index) =&gt;
      ( &lt;li key={index} className={tasks.task} &gt; &lt;InlineEdit properties={tasks} value={tasks.task} setValue={setTask} /&gt;
      &lt;img href=&#39;/&#39; alt={&#39;&amp;#10006;&#39;} onClick={()=&gt; removeItem(tasks,index)} /&gt;
      &lt;/li&gt;))
      }
    &lt;/ul&gt;
    :
    &lt;ul id=&#39;Worktasks&#39;&gt;

    { workTasks.map((tasks, index) =&gt;
      ( &lt;li key={index} className={tasks.task} &gt; &lt;InlineEdit properties={tasks} value={tasks.task} setValue={setTask} /&gt;
      &lt;img href=&#39;/&#39; alt={&#39;&amp;#10006;&#39;} onClick={()=&gt; removeItem(tasks,index)} /&gt;
      &lt;/li&gt;))
      }
    &lt;/ul&gt;}
  &lt;/section&gt;

答案1

得分: 1

  1. 永远不要在React中引用DOM或其函数(例如getElementsByClassName,removeElement等)。这是React应该为您执行的行为。您的JSX会在您的变量更新时更新。

  2. 我不确定您对UseMemo()的使用是否有任何用处。我已经更新了下面的行为,使其更易读。

  3. 或许可以使用name或一个唯一标识的属性作为removeTask函数的参数。

  4. 除了在<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="&#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 src="/" alt="&#10006;" />
              </button>
            </li>
          ))}
        </ul>
      )}
    </section>
  );
};

如果这对您有帮助,请告诉我!
1: https://stackoverflow.com/a/50736536/9108451

英文:

I recommend a few things

  1. 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.
  2. I'm not sure your uses of UseMemo() are doing anything useful. I've updated this behaviour below to be a bit more readable.
  3. Maybe use name or a uniquely identifying property as a param for your removeTask function.
  4. Never use onClick() except for inside a &lt;button /&gt; tag. Good news though: you can wrap your &lt;img /&gt; 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 &#39;react&#39;;
export const MyComponent = ({ personal = true, page = &#39;home&#39; }) =&gt; {
const [tasks, setTasks] = useState(JSON.parse(localStorage.getItem(&#39;Tasks&#39;)) || []);
const personalTasks = useMemo(() =&gt; tasks.filter(item =&gt; item.properties.personal), [tasks]);
const workTasks = useMemo(() =&gt; tasks.filter(item =&gt; !item.properties.personal), [tasks]);
function removeTask(name) {
setTasks(tasks.filter(item =&gt; item.name !== name));
}
return (
&lt;section&gt;
{personal &amp;&amp; page === &#39;home&#39; ? (
&lt;ul id=&quot;Personaltasks&quot;&gt;
{personalTasks.map((task, index) =&gt; (
&lt;li key={index} className={task.name}&gt;
{&#39; &#39;}
&lt;InlineEdit properties={task} value={task.name} setValue={() =&gt; {}} /&gt;
&lt;button type=&quot;button&quot; onClick={() =&gt; removeTask(task.name)}&gt;
&lt;img href=&quot;/&quot; alt={&#39;&amp;#10006;&#39;} /&gt;
&lt;/button&gt;
&lt;/li&gt;
))}
&lt;/ul&gt;
) : (
&lt;ul id=&quot;Worktasks&quot;&gt;
{workTasks.map((task, index) =&gt; (
&lt;li key={index} className={task.task}&gt;
{&#39; &#39;}
&lt;InlineEdit properties={task} value={task.name} setValue={() =&gt; {}} /&gt;
&lt;button type=&quot;button&quot; onClick={() =&gt; removeTask(task.name)}&gt;
&lt;img href=&quot;/&quot; alt={&#39;&amp;#10006;&#39;} /&gt;
&lt;/button&gt;
&lt;/li&gt;
))}
&lt;/ul&gt;
)}
&lt;/section&gt;
);
};

<!-- end snippet -->

Let me know if that's any good!

huangapple
  • 本文由 发表于 2023年3月7日 21:37:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/75662692.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定