I'm having troubles passing data from one context to the other in React. I have some job data that is received from a SignalR connection and I need to pass it to a specific job context, but I'm not sure how to do this.
I have the following code:
SignalRContext
export interface SignalRContextProps {
connect: () => void;
}
export const SignalRContext = createContext<SignalRContextProps>(null!);
export const useSignalRContext = (): SignalRContextProps => {
const {onProgressReceived} = useContext(JobsContext);
const connect = () => {
//Removed a lot of connection setup code for readability
const connection = new HubConnectionBuilder().build();
connection.on('JobReportProgress', onProgressReceived);
connection.start();
};
return {
connect,
};
};
SignalRContextProvider
type Props = {
children: ReactElement | ReactElement[];
}
export const SignalRContextProvider = (props: Props) => {
const {children} = props;
const signalRContext = useSignalRContext();
return (
<SignalRContext.Provider value={signalRContext}>
{children}
</SignalRContext.Provider>
);
};
JobsContext
export const JobsContext = createContext<JobsContextProps>(null!);
export const useJobsContext = (): JobsContextProps => {
const [jobs, setJobs] = useState<Job[]>([]);
const load = async (): Promise<void> => {
const jobs = await getAllJobs();
setJobs(jobs);
};
const onProgressReceived = (progress: JobProgress) => {
console.log(jobs);
const currentJob = jobs.find((job) => job.id === progress.id);
console.log(currentJob); //currentJob will always be empty because jobs array is NULL on receiving progress.
}
};
return {
load, onProgressReceived, jobs
};
};
JobsContextProvider
interface Props {
children: ReactElement | ReactElement[];
}
export const JobsContextProvider = (props: Props): ReactElement => {
const {children} = props;
const jobsContext = useJobsContext();
return (
<JobsContext.Provider value={jobsContext}>
{children}
</JobsContext.Provider>
);
};
index
ReactDOM.render(
<JobsContextProvider>
<SignalRContextProvider>
<App />
</SignalRContextProvider>
</JobsContextProvider>,
document.getElementById('root'),
);
Flow
- In my app.tsx I start the SignalR connection by calling
signalRContext.connect()created byconst signalRContext = useContext(SignalRContext); - I go to my job page where my 6 jobs are loaded from the backend via my context
const {load} = useContext(JobsContext);
await load();
- I trigger a job and I see that the SignalR context is calling onProgressReceived on the jobContext. But for some reason the jobs array is empty so I can't update the correct job. Is seems that a new context is created instead of reusing the existing context.
Anyone has an idea how I can make my SignalRContext pass data to my JobContext? Or maybe there is a better system that using context for this?
UPDATE 1:
I have the feeling that there is something strange going on with the HubConnection instance. When I register my 'onProgressReceived' function as a callback function on the 'JobReportProgress' event then it doesn't work. But when I first save the progress with setState and trigger the 'onProgressReceived' function with useEffect it seems to be working. Small example:
const {jobs, onProgressReceived} = useContext(JobsContext);
const [progress, setProgress] = useState<JobProgress | null>(null);
//Change the connection.on line with the following
connection.on('JobReportProgress', onReceived);
//And then we trigger the function on change
const onReceived = (progress: JobProgress) => {
setProgress(progress);
};
useEffect(() => {
if (progress !== null) {
onProgressReceived(progress);
}
}, [progress]);
This seems to be working, but not sure why I first need to save my progress with useState.