I'm currently using React with React Router and React Relay. Each of these has ways of updating/passing data. React with useState
, React Router with it's pushState wrapper (location.state
), and Relay with it's updater()
method.
I'm unsure however which I should be using to handle a global state (if any at all). There is a particular ID that all pages of my app need that is not a session. Imagine a form with let's say 5 steps. Step 1 has no ID, Step 2 gets the ID on successful submit, and now on steps 3-5 it needs that ID to be rendered into a hidden input (as an example). Let's call it formId
.
Currently, I'm passing it using React Router with location.state
but it feels wrong. Instead I feel like I should have a way to get this formId
once it's set. If formId
is null send them to Step 1 and if it's not null let the current page/route load.
Of course, I could use window.formId
but that seems to most wrong.
I ended up using React's Context API.
It looked something like this
// FormIdContext.tsx
import React, { createContext, useState, useContext } from 'react';
interface FormIdContextType {
formId: string | null;
setFormId: (formId: string | null) => void;
}
const FormIdContext = createContext<FormIdContextType | undefined>(undefined);
// A custom hook to consume the FormIdContext
export function useFormIdContext(): FormIdContextType {
const context = useContext(FormIdContext);
if (!context) {
throw new Error('useFormIdContext must be used within a FormIdContext.Provider');
}
return context;
}
export function FormIdProvider(props: React.PropsWithChildren<{}>) {
const [formId, setFormId] = useState<string | null>(null);
return (
<FormIdContext.Provider value={{ formId, setFormId }}>
{props.children}
</FormIdContext.Provider>
);
}
Then in my router
// App.tsx
import React, { useState } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import Step1 from './Step1';
import Step2 from './Step2';
import Step3 from './Step3';
import FormIdContext from './FormIdContext';
function App() {
const [formId, setFormId] = useState<string | null>(null);
return (
<Router>
<FormIdContext.Provider value={{ formId, setFormId }}>
<Route path="/step1" component={Step1} />
<Route path="/step2" component={Step2} />
<Route path="/step3" component={Step3} />
</FormIdContext.Provider>
</Router>
);
}
export default App;
Then I can get and set those values like this
// Step3.tsx
import React from 'react';
import { useHistory } from 'react-router-dom';
import { useFormIdContext } from './FormIdContext';
function Step3() {
const history = useHistory();
const { formId, setFormId } = useFormIdContext();
const handleFormSubmission = () => {
// Assuming you have the form data and you want to perform some submission logic
// Once the submission is successful and you get the formId, you can update the state
const newFormId = 'new_generated_form_id';
setFormId(newFormId);
// Optionally, you can navigate to another page or perform other actions
// Here, we simply go back to Step2 as an example
history.push('/step2');
};
return (
<div>
{/* Your Step 3 content that may use formId */}
<h1>Step 3</h1>
<p>Current formId: {formId}</p>
<button onClick={handleFormSubmission}>Submit Form</button>
</div>
);
}
export default Step3;