How to pass a function to link component in react router v6

Viewed 139

There is a function that I want to pass to a link component. How would I do it? I tried using state and useLocation and while they work good for normal variables, useLocation sends null when trying to pass down a function.

const function=someFunction(){}

<Link state={func:function} to="/LoginScreen" style={style.a} >
  Login
</Link>

This does not work.

Code trying to use a React context:

import React, { useState } from "react";
import { Link } from "react-router-dom";

const RefreshDisplayContext = React.createContext();

function Launchpad() {
  const [launchpadState, updatelaunchpadState] = useState({
    userIsLoggedIn: false
  });

  function refreshDisplay(event) {
    updatelaunchpadState({ userIsLoggedIn: true });
  }

  return (
    <RefreshDisplayContext.Provider value={refreshDisplay}>
      <div>
        {launchpadState.userIsLoggedIn ? (
          <p>User is Logged In </p>
        ) : (
          <p>User is Not Logged In </p>
        )}

        <Link to="login" state={launchpadState}>
          Login via Child
        </Link>
      </div>
    </RefreshDisplayContext.Provider>
  );
}

export { Launchpad, RefreshDisplayContext };
import React, { useState } from "react";
import { RefreshDisplayContext } from "../routes/Launchpad";

function Login(props) {
  const refreshDisplay = React.useContext(RefreshDisplayContext);

  return (
    <button
      type="button"
      onClick={() => refreshDisplay({ userIsLoggedIn: true })}
    >
      Login
    </button>
  );
}

export { Login };
import ReactDOM from "react-dom/client";
import { BrowserRouter, Routes, Route } from "react-router-dom";

import { Launchpad } from "./routes/Launchpad";
import { Login } from "./routes/Login";

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Launchpad />} />
      <Route path="/login" element={<Login />} />
    </Routes>
  </BrowserRouter>
);

Codesandbox

1 Answers

Issue

The issue with the code is that you are only rendering the RefreshDisplayContext around the UI/ReactTree that the Launchpad component is rendering. When the link to "/login" is clicked and the app navigates to that route and renders Login, the component being rendered is outside the React context it's trying to access, and since there's also no default value, refreshDisplay can't be accessed from the undefined context value.

Solution

Move the RefreshDisplayContext.Provider higher in the ReactTree so that it is able to provide the same context value to all consumers.

Create a new file to declare and export the context.

refreshDisplay.context.js

import { createContext, useContext, useState } from "react";

export const RefreshDisplayContext = createContext({
  refreshDisplay: () => {}
});

export const useRefreshDisplay = () => useContext(RefreshDisplayContext);

const RefreshDisplayProvider = ({ children }) => {
  const [launchpadState, updatelaunchpadState] = useState({
    userIsLoggedIn: false
  });

  function refreshDisplay(event) {
    updatelaunchpadState({ userIsLoggedIn: true });
  }

  return (
    <RefreshDisplayContext.Provider value={{ launchpadState, refreshDisplay }}>
      {children}
    </RefreshDisplayContext.Provider>
  );
};

export default RefreshDisplayProvider;

Import and wrap all the components you want to have access to the RefreshDisplayContext.

index.js

import ReactDOM from "react-dom/client";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import RefreshDisplayProvider from "./refreshDisplay.context";
import { Launchpad } from "./routes/Launchpad";
import { Login } from "./routes/Login";

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
  <BrowserRouter>
    <RefreshDisplayProvider>
      <Routes>
        <Route path="/" element={<Launchpad />} />
        <Route path="/login" element={<Login />} />
      </Routes>
    </RefreshDisplayProvider>
  </BrowserRouter>
);

Update the consumers to access the provided context value.

Launchpad

import React from "react";
import { Link } from "react-router-dom";
import { useRefreshDisplay } from "../refreshDisplay.context";

export function Launchpad() {
  const { launchpadState } = useRefreshDisplay();

  return (
    <div>
      <p>
        User is {launchpadState.userIsLoggedIn ? "Logged In" : "Not Logged In"}
      </p>

      <Link to="login" state={launchpadState}>
        Login via Child
      </Link>
    </div>
  );
}

Login

import React from "react";
import { useNavigate } from "react-router-dom";
import { useRefreshDisplay } from "../refreshDisplay.context";

function Login(props) {
  const navigate = useNavigate();
  const { refreshDisplay } = useRefreshDisplay();

  return (
    <button
      type="button"
      onClick={() => {
        refreshDisplay();
        navigate("/", { replace: true });
      }}
    >
      Login
    </button>
  );
}

export { Login };

Edit how-to-pass-a-function-to-link-component-in-react-router-v6

Related