ReactJs - Initialize Redux Store from URL query parameters

Viewed 3552

I'm extremely new to ReactJs and Redux. I am able to set up a simple app with multiple React components dispatching actions to the Redux store on button clicks, dropdown selections, etc. But one thing I can't seem to figure out is, how would you go about initializing an app's state based on a URL parameter?

For example, let's say you have a user profile page, and depending on the "userid" URL parameter, you want to display different user data on the page when it first loads. What I initially thought was to use the "componentDidMount" lifecycle method and dispatch an action to the store - maybe something like "loadUser(userid)". But this seems like overkill - is there a universally-accepted way of populating a store from some initial value?

4 Answers

If you are needing route specific variables then its probably easiest (and best practice) to use the route params to initialize state and rerun actions.

If you use class based components loadUser(userId) will need to be in both componentDidMount and componentDidUpdate.

For functional components there is React hook useEffect() instead (which seems to be becoming best/better practice these days).

For Example:

If you are using react-router-dom in your routes.js/app.js you will have a route such as:

<Route path='/some/path/:userid' component={someComponent}/>

Then use props.match.params and useEffect with the userid as a dependency:

import React , {useEffect} from 'react'
import {connect} from 'react-redux'
import * as actions from 'path/to/redux/actions'

// destructure `match.params.userid`, redux `user` and redux action `loadUser` from props
const someComponent = ({ match:{params:{userid}} , user ,  loadUser}) => {

    useEffect(()=>{
      userid && loadUser(userid)
    },[loadUser , userid]) // runs after first mount and every time userid changes

    return <div>{user && user.id}</div>
}

connect(
    ({user})=>({user}),
    actions
)(someComponent)

so first in your container component(any page/route) you can get the query string from the URL and update the redux state accordingly

Example:

import queryString from 'querystring';

const App (props){

//semulate the component did mount behavior 
useEffect(()=>{
     const {
     active_section,
     tab,
    //or what ever
    } = queryString.parse(history.location.search.replace('?', ''));

  props.updateStore({
    active_section,
    tab
   })
},[])

export default connect(App)

I think you can make use of Switch and Route components where you can decide the url and params to be passed to the component. Once you're done with this then you can decide in the component itself to update the state in store.

But for initializing the store you can pass the generic state i.e. an empty state object or your home page state.

Hope this helps!!

This example uses import { Record } from immutable to save the initial state. If you want to update that from a query string, then you would call an action to update the state just like anything else. I would probably call the action in the constructor or componentDidMount().

  /*
  *
  * CourseContainer reducer
  *
  */

  import { Record } from 'immutable';
  import {
     FETCH_COURSE_FROM_QUERYSTRING,
     FETCH_COURSE_FROM_QUERYSTRING_SUCCESS
  } from './constants';

  const CourseContainerRecord = Record({
     course: {
        CourseTitle: '',
        AhecId: '',
        SeatsRemaining: 0,
        Price: 0,
        Hidden: true,
        Canceled: false,
        Objectives: '',
        CreditHours: '',
        Description: '',
     },
     coordinators: [],
  });

  export const initialState = new CourseContainerRecord({});

  function courseContainerReducer(state = initialState, action) {
     switch (action.type) {
        case FETCH_COURSE_FROM_QUERYSTRING_SUCCESS:
        // ... code
        default:
           return state;
     }
  }

  export default courseContainerReducer;

  /*
  *
  * CourseContainer actions
  *
  */
  import toastr from 'toastr';

  import {
     FETCH_COURSE_FROM_QUERYSTRING,
     FETCH_COURSE_FROM_QUERYSTRING_SUCCESS,
  } from './constants';

  // #region GET course
  export function fetchCourseFromQuerystring(courseId) {
     return {
        type: FETCH_COURSE_FROM_QUERYSTRING,
        courseId,
     };
  }

  export function fetchCourseFromQuerystringSuccess(course) {
     return {
        type: FETCH_COURSE_FROM_QUERYSTRING_SUCCESS,
        course,
     };
  }

EDIT I can add an example of dispatching if you need one.

Related