Using React, React Router, and React Relay which should I use to store and retrieve state?

116 Views Asked by At

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.

1

There are 1 best solutions below

0
Oscar Godson On BEST ANSWER

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;