1

I am making a react app (not react-native) using React-v17, and react-redux V7, and React Router V6, searching and reading many articles, I could not find a way to navigate programmatically inside redux actions using the V6 hooks, as hooks can only be called inside components, here is what I have

registerPage.jsx

import React, { Component } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'

import { LockClosedIcon } from '@heroicons/react/solid'
import Loader from 'react-loader-spinner'

import { registerUser } from '../../../state_man/store/register'

const RegisterForm = (props) => {
  const [registerFields, setRegisterFields] = useState({})
  const [errors, setErrors] = useState({})
  const [validInput, setValidInput] = useState({})
  const [loading, setLoading] = useState(false)

  let navigate = useNavigate()
  let params = useParams()

  let auth = useSelector((state) => state.auth)
  let mainUi = useSelector((state) => state.UI.mainUI)
  let registerUi = useSelector((state) => state.UI.registerPage)

  const dispatch = useDispatch()

  const { isLoggedIn } = auth

const onRegisterUser = (e) => {
    e.preventDefault()

    const credentials = {
      username: registerFields['username'],
      ['phone']: registerFields['phone'],
      email: registerFields['email'],
      region: registerFields['region'],
      password: registerFields['password'],
      address: registerFields['address'],
      client_name: registerFields['client_name'],
    }
    dispatch(registerUser(credentials))
return (

    // my form is so long, i made it short just for demonstration purposes! 
    <form>
      <input type='text' />
      <input type='passowrd' />
      <button
        onClick={(e) => {
          // here i call the function that does the registration ,
 //that included calling a function that dispatches API action,
          onRegisterUser(e)
        }}
      ></button>
    </form>
  )

  }

in my state management module, I create a function that dispatch API actions, what

register.js

const url = '/client'
export const registerUser = (credentials) => {

  return apiActionCreators.apiCallBegan({
    url,
    method: 'post',
    data: credentials,
    onStart: START_LOADING.type,
    onEnd: END_LOADING.type,
    onSuccessFunc: (data) => (dispatch, store) => {
      dispatch(USER_REGISTERED(data))
      dispatch(CLEAR_MAIN_ERROR())
      // i want to take the user to a specific page using something like navigate("menu")
    },
    onErrorFunc: (error) => (dispatch, store) => {
      dispatch(ADD_MAIN_ERROR(error))
    },
  })
}

as I commented, I want to execute a function in that action inside "onSuccessFunc" so that it takes the user to a specific page , i know i can make a reducer and use it for navigation, i send action with a payload to where i want to navigate, and in a HOC, i check if there is a path to navigate to, if i true, i clear it and i navigate , some thing like this !

let navigation = useSelector((state) => state.navigation)

  useEffect(() => {
    if (navigation.path) {
      dispatch(
        navigationActions.CLEAR_NAVIGATION_PATH()
      )
      navigate('/navigation.path')
    }
  }, []) 

this does seem to be a good solution, and i think there can be a lot of DRY and shortcomings, what is the best way to do this! and do you see any shortcomings in my solution!, remember i am using react-router V6! thanks.

Drew Reese
  • 103,803
  • 12
  • 69
  • 96
  • Does https://stackoverflow.com/a/70000286/8690857 or https://stackoverflow.com/a/70012117/8690857 answer your question? The first is the general solution for exposing out programmatic navigation outside the router and React app, the second is a more specific use case, you can swap the usage in axios interceptors for usage in your asynchronous action creators. – Drew Reese Jan 31 '22 at 17:03
  • yes they do, yet i thought i would find a middleware or something provided by RRDv6 itself, and it seems what they have is "unstable", and I find the first answer to create my hooks also good, so thank you, I ll accept the first answer, and make a comment. – Mustapha Khial Jan 31 '22 at 22:14
  • [connected-react-router](https://github.com/supasate/connected-react-router) is a solid solution if you decide to rollback to RRDv5, though hopefully there's an update to allow compatibility with RRDv6. It may be worth keeping an eye on in case they do. – Drew Reese Jan 31 '22 at 22:18

2 Answers2

1

You'd better do the navigation directly in your callback. You can take advantage of the rule of Hooks: Call Hooks from custom Hooks.

// register.js or useRegister.js

export const useRegisterUser = () => {

  const navigate = useNavigate();

  const doRegister = (credentials) => {
    return apiActionCreators.apiCallBegan({
    url,
    method: 'post',
    data: credentials,
    onStart: START_LOADING.type,
    onEnd: END_LOADING.type,
    onSuccessFunc: (data) => (dispatch, store) => {
      dispatch(USER_REGISTERED(data))
      dispatch(CLEAR_MAIN_ERROR())
      // allowed since it's inside a hook
      navigate("menu")
    },
    onErrorFunc: (error) => (dispatch, store) => {
      dispatch(ADD_MAIN_ERROR(error))
    },
  })
}

return {
    doRegister 
}

}
// registerPage.js

import { useRegisterUser } from './register.js'
// other imports 

const RegisterForm = (props) => {

  const { doRegister } = useRegisterUser()

// other stuffs
// grab credentials

    dispatch(doRegister(credentials))

// other stuffs

return (<></>)
}
exaucae
  • 1,206
  • 1
  • 8
  • 18
0

after searching and reading some comments, i found 3 possible solutions, 1st is the first answer yet I was not quite comfortable using it as I did not know the implementation details of hooks, I tried it and it worked well, 2nd option is to create your own CustomRouter, give it a history object that you create, and make changes to the history object you made as specified here: https://stackoverflow.com/a/70012117/8690857, thanks to Drew for pointing it out. 3rd is to use an object that you could import from RRDv6, and is called unstable_HistoryRouter , and you can find it here also https://stackoverflow.com/a/70012117/8690857.