在响应路由器更改时维护每个子组件的上下文数据

maintain the context data for each child component on react router change

我是 React 的初学者,正在开发 React 应用程序,我使用上下文来维护按钮状态,按钮状态可以处于开始、加载、停止的任何一个阶段。

我正在将上下文传递给应用程序组件,并有一个反应路由器根据路由渲染组件。我通过循环遍历每张卡片都有一个按钮组件的数据来渲染卡片组件。

单击 card1 按钮时,按钮 1 应进入加载阶段 10-15 秒,具体取决于 api 响应时间。一旦响应到来,它应该处于停止阶段。如果一起单击,对于 button2 和 button3 也是类似的。现在,当我立即单击 button1 和 button2 时似乎工作正常

但是当我同时单击 2 个按钮并移动到另一条路线并快速返回时,我没有看到我的按钮处于加载状态,尽管 api 响应仍在等待中。我应该看到它们处于加载状态,当响应到来时,我应该看到它们处于开始或停止阶段。

我知道我可以使用本地或会话存储,但由于某些代码限制我不想这样做。任何帮助将不胜感激。

这是堆栈闪电战 link : https://stackblitz.com/edit/node-3t59mt?file=src/App.js Github Link: https://github.com/mehulk05/react-context-api

Button.jsx

import React, { useContext,useEffect, useState } from 'react'
import DbContext from '../Context/sharedContext'

function Button(props) {
    console.log(props)
    const {
        requestStartDbObj,
        setRequestStartDbObj
    } = useContext(DbContext)
    const [state, setstate] = useState(props.text?.status ?? "start")
    
    useEffect(() => {
        setstate(props.text?.status??"start")
        return () => {
            
        }
    }, [state])
    console.log(requestStartDbObj)
    const start = ()=>{
        setRequestStartDbObj({id:props.status.id, status:"loading"})
        setstate("loading")
        
        setTimeout(()=>{
            setstate("stop")
            setRequestStartDbObj({id:props.status.id, status:"stop"})
        },5000)
    }
    return (
        <div>
            <button onClick={start}>{state}1</button>
        </div>
    )
}

export default Button

Card.jsx

function Card(props) {
    const {
        requestStartDbObj,
    } = useContext(DbContext)
    return (
        <div>
            <h1>{props.data.name}</h1>
            <Button status={props.data} text={requestStartDbObj} />
        </div>
    )
}

Component1.jsx

function Component1() {
  let data = [
    {
      id: 1,
      name: "card1",
      status: "start",
    },
    {
      id: 2,
      name: "card2",
      status: "start",
    },
    {
      id: 3,
      name: "card3",
      status: "start",
    },
  ];
  return (
    <div>
      <h1>Hello</h1>
      {data.map((d, i) => (
        <Card key={i} data={d} />
      ))}
    </div>
  );
}

ComponentWrapper.jsx

  <h3>Wrpper</h3>
            <Routes>
                <Route path="/" element={<Component1 />} />
                <Route path="about" element={<Component2 />} />
            </Routes>
  </div>

App.js

function App() {
  return (
    <div className="App">
       <BrowserRouter>
    
       <Link to="/">Home</Link> <br></br>
       <Link to="/about">Comp 2</Link>
       <DbProvider>
        <ComponentWrapper/>
      </DbProvider>
       </BrowserRouter>
      
    </div>
  );
}

问题是您的 DbProvider 上下文不是状态的真实来源,它不是维护 requestStartDbObj 状态的组件。每个 Button 都在本地复制状态并使用自己的 start 函数。每个Button也是替换上下文的requestStartDbObj状态,所以当切换回主路径时所有按钮获得相同的初始状态值。离开主路径后,Button 组件将被卸载,因此超时时的状态更新将丢失。

您应该将 start 逻辑移至 sharedContext,以便 DbProvider 保持对状态更新的控制。 start 应该使用一个 id 参数,以便它可以正确更新该特定对象的 status

DbProvider

const DbProvider = (props) => {
  const [requestStartDbObj, setRequestStartDbObj] = useState({});

  const { children } = props;

  const start = (id) => {
    setRequestStartDbObj((state) => ({
      ...state,
      [id]: { status: "loading" }
    }));

    setTimeout(() => {
      setRequestStartDbObj((state) => ({
        ...state,
        [id]: { status: "stop" }
      }));
    }, 5000);
  };

  return (
    <DbContext.Provider
      value={{
        requestStartDbObj,
        start
      }}
    >
      {children}
    </DbContext.Provider>
  );
};

卡片

仅将映射时从 Component1 传递的 data 道具传递给 Button id 道具。

function Card({ data }) {
  return (
    <div>
      <h1>{data.name}</h1>
      <Button id={data.id} />
    </div>
  );
}

按钮

使用 id 属性传递给上下文提供的 start 函数。还可以使用 id 访问当前状态。

function Button({ id }) {
  const { requestStartDbObj, start } = useContext(DbContext);

  return (
    <div>
      <button onClick={() => start(id)}>
        {requestStartDbObj[id]?.status || "start"}-{id}
      </button>
    </div>
  );
}