Reputation: 31
I am trying to handle individual promises in a React app, and list their current state in a list on the App. I have two components :
<Instruction/>
components based on InstructionListContextMy problem is the following : One instruction behaves perfectly fine, but when I add another instruction in the list, timeouts seem to overlap, leading instructions that are still pending to disappear from the list as well ! Do you see anything wrong in my way of handling it ?
Here's the code for reference :
import { useContext } from "react";
import InstructionListContext from "../../const/contexts/instructionListContext";
import { instruction } from "../../const/models/instruction";
import Instruction from "./instruction";
import './instructionList.scss';
export default function InstructionList() {
const {instructionList} = useContext(InstructionListContext)
return (
<div id="instruction-list">
<h1>Instructions en cours</h1>
{instructionList.map((instruction:instruction) => {
return <Instruction key={instruction.id} instruction={instruction} />
})}
</div>
)
}
import { useContext, useEffect, useState } from "react"
import failure from "../../assets/picto/failure.svg"
import loading from "../../assets/picto/loader.svg"
import success from "../../assets/picto/success.svg"
import { instruction, InstructionActionTypes } from "../../const/models/instruction"
import { message, messageType } from "../../const/models/message"
import InstructionListContext from "../../const/contexts/instructionListContext"
interface IProps {
instruction: instruction,
}
enum instructionStates {
success = 1,
failure = 2,
pending = 3
}
export default function Instruction({ instruction }: IProps) {
const [state, setState] = useState<instructionStates>(instructionStates.pending)
const [statePic, setStatePic] = useState(generateStatePic(state))
const [actionLabel] = useState(generateActionLabel(instruction.action))
const {instructionList, setInstructionList} = useContext(InstructionListContext)
useEffect(() => {
instruction.promise.then((message:message) => {
message.type === messageType.confirm ? setState(instructionStates.success) : setState(instructionStates.failure)
})
}, [])
useEffect(() => {
setStatePic(generateStatePic(state))
let timeout:NodeJS.Timeout
if (state === instructionStates.success || state === instructionStates.failure) {
if (instruction.cleanup) {
instruction.cleanup()
}
timeout = setTimeout(() => {
const filtered = instructionList.filter(x => x.id !== instruction.id)
setInstructionList([...filtered])
}, 5000)
}
return () => {
clearTimeout(timeout)
}
}, [state])
return (
<div className="instruction">
<p>{statePic}</p><p>{actionLabel}</p><p>dans {instruction.parentId}</p>
</div>
)
}
const generateActionLabel = (instructionType: InstructionActionTypes): JSX.Element => {
let source: string = "ND"
switch (instructionType) {
case InstructionActionTypes.elementCreation:
source = "Nouv. Élém."
break
case InstructionActionTypes.elementUpdate:
source = "Mod. Élém."
break
case InstructionActionTypes.elementDeletion:
source = "Suppr. Élém."
break
case InstructionActionTypes.subelementCreation:
source = "Nouv. Sous-élém."
break
case InstructionActionTypes.subelementUpdate:
source = "Mod. Sous-élém."
break
case InstructionActionTypes.subelementDeletion:
source = "Suppr. Sous-élém."
break
}
return <p>{source}</p>
}
const generateStatePic = (instructionState: instructionStates): JSX.Element => {
let source: string = loading
switch (instructionState) {
case instructionStates.pending:
source = loading
break
case instructionStates.failure:
source = failure
break
case instructionStates.success:
source = success
break
}
return <img src={source}/>
}
Before the given version of my code, I didn't give a key prop to my Instruction component in the .map(), which I thought could be the source of my problem. (spoiler, it was not)
I also tried passing the timeout from instruction list instead of creating it in <Instruction/>
, but it did not work either...
Edit after Jesus Diaz Rivero's comment: Here is the code of InstructionListContext.tsx :
import { createContext } from "react";
import { instruction } from "../models/instruction";
interface IInstructionListContext {
instructionList: instruction[],
setInstructionList: (instruction: instruction[]) => void,
}
const InstructionListContext = createContext<IInstructionListContext>({
instructionList:[],
setInstructionList:()=>{},
})
export default InstructionListContext
And here is my instruction.ts type declaration :
import { message } from "./message"
export enum InstructionActionTypes {
elementCreation = 1,
elementDeletion = 2,
elementUpdate = 3,
subelementCreation = 4,
subelementDeletion = 5,
subelementUpdate = 6}
export type instruction = {
id: string
promise:Promise<message>
action:InstructionActionTypes
parentId:string
cleanup?:()=>any
}
Upvotes: 0
Views: 43