上下文Api状态没有改变

问题描述

我试图从一个使用useContext钩子消耗上下文的组件中调用一个在Context Provider内部的名为deleteTask的函数,该函数在上下文提供者的状态下从数组中删除某个项目,但是当我这样做时,提供程序的状态根本没有改变,我尝试解决该问题,该函数执行,但是好像它是否在复制的提供程序范围内执行?还尝试了添加任务的功能,即时通讯有相同的问题。我还添加一个用于设置活动任务的功能,我不知道为什么一个工作了,而其他人却没有。我真的不知道发生了什么,这是代码,pleeeeease可以帮助我:

tasks-context.jsx

import React from 'react';
import './assets/styles/gestion-style.css';
import './assets/styles/icons.css';

import { TasksProvider } from '../../Context/tasks-context';

import TaskContainer from './components/taskContainer.jsx';

function Main( props ) {
  
 return (
      <TasksProvider>
        <TaskContainer />
      </TasksProvider>
    );
}

“主要”

Main.jsx

import React,{ useContext,useEffect } from 'react';
import TaskTab from './TaskTab';


import { TasksContext } from '../../Context/tasks-context';

function TaskContainer( props ) {
  const { tasks } = useContext( TasksContext );

  return (
    <div className="Boxes" style={{ maxWidth: '100%',overflow: 'hidden' }}>
      {tasks? tasks.map( taskTab=>
        ( <TaskTab task={taskTab.task} isActive={taskTab.isActive} key={taskTab.key} taskTabKey={taskTab.key} /> ))
        :
        null
      }
    </div>
  );
}

export default TaskContainer;

任务容器映射任务数组:

TaskContainer.jsx

import React,{ useContext } from 'react';

import { TasksContext } from '../../Context/tasks-context';

function TaskTab( props ) {
  let { task,isActive,taskTabKey } = props;
  const { handleSelectTask,deleteTask } = useContext( TasksContext );

  const selectTask = ()=>{
    handleSelectTask( task,taskTabKey );
  };

  const handleDelete = () =>{
    deleteTask( taskTabKey );
  };

  return (
    <div onClick={ selectTask }>
      <article className={`${task.type} ${isActive ? 'active' : null}`}>
        <p className="user">{task.text}</p>
        <button onClick={handleDelete}>
          <i className="icon-close"></i>
        </button>
      </article>
    </div>
  );
}

export default TaskTab;

以及我从中调用删除的上下文函数的任务组件:

TaskTab.jsx

echo $item;

解决方法

卢卡斯,这不是上下文或提供程序的问题。

您面临的问题实际上是一种称为事件冒泡的机制,其中先执行当前处理程序,再执行父处理程序。

可以在此处找到有关事件冒泡的更多信息。 https://javascript.info/bubbling-and-capturing

在您的情况下,首先调用 handleDelete 函数,然后调用 handleSelect 函数。

解决方案: event.stopPropagation();

将handleDelete和handleSelect函数更改为此

  const selectTask = () => {
    console.log("handle select called");
    handleSelectTask(task,taskTabKey);
  };

  const handleDelete = event => {
    console.log("handle delete called");
    event.stopPropagation();
    deleteTask(taskTabKey);
  };

现在检查您的控制台,您会发现只有被调用的handle delete会打印,这有望解决您的问题。

如果仍然无法使用,请告诉我。我将为您创建一个codeandbox版本。

快乐编码。

,

谢谢你的提问!

这里发生的事情令人困惑,这是可以理解的,我花了一段时间才自己意识到。

TL; DR:由于事件传播,每次单击handleSelectTask的按钮时,都会调用Provider中的deleteTaskhandleSelectTask并未使用deleteTask修改过的状态,即使它在后面运行,因为它已经封闭了初始tasks数组。


快速解决方案1 ​​

阻止事件从删除按钮的传播传播到TaskTab div的传播,这可能是所需的行为。

// in TaskTab.jsx
const handleDelete = (event) => {
  event.stopPropagation(); // stops event from "bubbling" up the tree
  deleteTask(taskTabKey);
}

在DOM中(事件也由React模拟),事件在树上“冒泡”,以便父节点可以处理来自其子节点的事件。在示例中,<button onClick={handleDelete}><div onClick={selectTask}>的子元素,这意味着当从按钮触发click事件时,它将首先像我们想要的那样调用handleDelete函数,但是之后,它还会 从父div调用selectTask函数,这可能是意外的。您可以在MDN上了解有关事件传播的更多信息。


快速解决方案2

写状态更新以在调用它们时使用中间状态值。

// in tasks-context.jsx
const deleteTask = ( taskToDeleteKey ) => {
  setActiveTask(null);
  // use the function version of setting state to read the current value whenever it is run
  setTasks((stateTasks) => stateTasks.filter(task => task.key !== taskToDeleteKey));
}

const handleSelectTask = ( taskToSelect,key ) =>{
  setActiveTask( taskToSelect );
  // updated to use the callback version of the state update
  setTasks((stateTasks) => stateTasks.map( task => {
    // set the correct one to active
  }));
};

使用setTasks状态更新的回调版本,它将实际上在应用更新时(包括(尤其是在更新中间!))读取值,从{{ 1}}之后被调用,这意味着它实际上看到了先被handleSelectTask修改过的数组!您可以在React文档(hooks) (setState)中了解有关设置状态的此回调变体的更多信息。请注意,此“修复”将意味着即使任务已删除,您的组件仍将调用deleteTask。请注意,它不会有任何不良影响。


让我们更详细地了解正在发生的事情:

首先,从handleSelectTask创建tasks变量。整个组件都使用了相同的变量,这完全正常。

useState

麻烦所在的地方是,如果两个函数都试图在同一渲染周期中更新相同的状态值,那么即使其他函数尝试了,它们都将引用任务数组的原始值。更新状态值!在您的情况下,由于// created here const [ tasks,setTasks ] = useState( dummyTasks ); const [ activeTask,setActiveTask ] = useState(); const deleteTask = ( taskToDeleteKey ) =>{ setActiveTask( null ); // referenced here,no big deal setTasks( tasks.filter( task => task.key !== taskToDeleteKey )); }; const handleSelectTask = ( taskToSelect,key ) =>{ setActiveTask( taskToSelect ); // tasks is referenced here,too,awesome const newTaskArray = tasks.map( task => { if( task.key === key ){ task.isActive = true; }else{ task.isActive = false; } return task; }); setTasks( newTaskArray ); }; 之后 handleSelectTask之后运行,这意味着deleteTask将使用未修改的数组更新状态!当它运行时,它仍然会在数组中看到两个项目,因为handleSelectTask变量在实际提交更新并重新发布所有内容之前不会更改。这样一来,似乎删除部分就无法正常工作,实际上是由于tasks不知道删除是在删除之前发生的,所以实际上只是放弃了它的作用。