How to programatically route in React within a state?

Viewed 790

I'm new to React and using Zustand to handle global stores. My goal in this particular instance is to route to an "RSVP" page if the correct user is found.

Scenario A: User is found in the database. Route to new page

Scenario B: User is not found in the database. Stay on current page and display error message

It seems straightforward if you want to achieve this on click, however I can't find any information on how to trigger it within a state.

My understanding is that useHistory can be used, but only within a function, so it doesn't work when I try to use it in a state. eg.

import create from 'zustand';
import { mountStoreDevtool } from 'simple-zustand-devtools';
import { useHistory } from "react-router-dom";


const useStore = create((set, get) => ({
  guests: [],
  currentGuests: [],
  message: "Please enter your unique password found in your email.",
  returnDatabase: (input) => {
    const guests = get().guests;

    const targetParty = guests.filter((guest) => guest.party === input);
    
    if (targetParty.length !== 0) {
      set({ currentGuests: targetParty });
      const setMessage = "Please enter your unique password found in your email.";
      set({ message: setMessage });

      //I want to programatically redirect here
      let history = useHistory();
      history.push("/rsvp");

    } else {
      const setMessage = "Your password has not been found. Please check your email and try again.";
      set({ message: setMessage });
    }
  
  },
  setStore: (data) => {
    const guestsData = data.guests;
    set({ guests: guestsData });
  },
}));

However, I'm receiving this error:

Failed to compile
src/store/storeUtil.js
  Line 19:21:  React Hook "useHistory" is called in function "returnDatabase" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter  react-hooks/rules-of-hooks

Search for the keywords to learn more about each error.

I figure this is because it's not in a function, so I tried to put it in a separate component:

import create from 'zustand';
import { mountStoreDevtool } from 'simple-zustand-devtools';
import { useHistory } from "react-router-dom";

  export function RsvpButton(){
    let history = useHistory();
  
    function handleClick() {
      history.push("/rsvp");
    }
  
    return (
      {handleClick}
    );
  }


const useStore = create((set, get) => ({
  guests: [],
  currentGuests: [],
  message: "Please enter your unique password found in your email.",
  returnDatabase: (input) => {
    const guests = get().guests;

    const targetParty = guests.filter((guest) => guest.party === input);
    
    if (targetParty.length !== 0) {
      set({ currentGuests: targetParty });
      const setMessage = "Please enter your unique password found in your email.";
      set({ message: setMessage });

      //try to use component here 
      <RsvpButton />

...etc

And this just doesn't do anything/have any errors. Should I be using a different method to route to the RSVP page other than useHistory? Or am I doing something wrong in my implementation?

2 Answers

Your button component RsvpButton is not creating a button.

The code for function RsvpButton should be something like

export function RsvpButton () {
    ... 
    return (
        <button onClick={handleClick}>Rsvp</button>
    ) 
}

That way, the handleClick function will be linked to the onClick property of the button, which will make it run upon clicking the button.

Solution for anyone having a similar issue:

To solve this, I put the useHistory handler in the parent component RsvpContainer.js

import { useHistory } from 'react-router-dom';
    useEffect(() => {
      loadData();
    });
    return (
        <React.Fragment>
            <div className="header">
                <div className="Bg section-2"></div>
                <div className="divider"></div>
                <div className="App section-2">
                    <h3>RSVP.</h3>
                    <h2>Saturday 30 October, 2021</h2>
                    <Input 
                        filter={filter} 
                        setFilter={setFilter}
                        returnDatabaseHandler={returnDatabaseHandler}
                        onUserFound={onUserFoundHandler}
                    />
                        
                </div>
            </div>
        </React.Fragment>
    )

Input.js

export function Input({ setFilter, filter, onUserFound }) {
  const isUserFound = useStore((state) => state.isUserFound);

  useEffect(() => {
    if (isUserFound) {
      onUserFound();
    }
  }, [isUserFound]);

}

storeUtil.js (zustand global store) - I added this in 'returnDatabase'

  set({
    message: setMessage,
    isUserFound: true
  });
Related