Search code examples
reactjsreduxreact-router-redux

Access query parameters in async call (react redux)


My react redux (4.4.5) project uses react-router-redux(4.0.5) and redux-async-connect (0.1.13). Before I load my container component, I want to asynchronously load data from my API. The url contains a query parameter named "category" which is used to fetch the messages. ie. user/cornel/messages?category=react-redux

The parameters linked to my location/path are in state.routing.locationBeforeTransitions, but these are not up to date when in the async call. I can get the path parameters from the params parameter that is passed to the async function, but this does not contain the query parameters.

@statics({
  reduxAsyncConnect(params, store) {
    const { dispatch } = store;
    return Promise.all([
      dispatch(loadMessages(category)) <-- need the query parameter "category" here
    ]);
  }
})
@connect(state => ({
  messages: state.user.messages
}))
export default class HomeContainer extends Component {
  static propTypes = {
    dispatch: PropTypes.func
    messages: PropTypes.array.isRequired
  };

  render() {
    const { messages } = this.props;

    return (
      ...
    }
  }
}

Anyone has any idea how I should access the query parameter so it works both client and server side? Thanks in advance!


Solution

  • You should be able to get search from redux state as the following if you are using react-redux-router.

    @statics({
      reduxAsyncConnect(params, store) {
        const { dispatch } = store;
        return Promise.all([
          dispatch(loadMessages(category)) <-- need the query parameter "category" here
        /* you might get 
          store.getState().
              routing.locationBeforeTransitions.search 
          from here too */
        ]);
      }
    })
    @connect(state => ({
      messages: state.user.messages,
      /* get search from redux state */
      search : state.routing.locationBeforeTransitions.search
    }))
    export default class HomeContainer extends Component {
      static propTypes = {
        dispatch: PropTypes.func
        messages: PropTypes.array.isRequired
      };
    
      render() {
        const { messages } = this.props;
    
        return (
          ...
        }
      }
    }
    

    Let me know if it is not available for you.

    EDIT

    Here is a piece of code that doesn't use reduxAsyncConnect and accomplishing what you want to do.

    // CONSTANTS
    const
      GET_SOMETHING_FROM_SERVER = 'GET_SOMETHING_FROM_SERVER',
      GET_SOMETHING_FROM_SERVER_SUCCESS = 'GET_SOMETHING_FROM_SERVER_SUCCESS',
      GET_SOMETHING_FROM_SERVER_FAIL = 'GET_SOMETHING_FROM_SERVER_FAIL';
    
    // REDUCER
    const initialState = {
      something : [],
      loadingGetSomething: false,
      loadedGetSomething:false,
      loadGetSomethingError:false
    };
    
    export default function reducer(state = initialState, action) {
    
      switch(action.type) {
    
        case GET_SOMETHING_FROM_SERVER:
          return Object.assign({}, state, {
            loadingGetSomething: true,
            loadedGetSomething:false,
            loadGetSomethingError:false
            something : [] // optional if you want to get rid of old data
          });
        case GET_SOMETHING_FROM_SERVER_SUCCESS:
          return Object.assign({}, state, {
            loadingGetSomething: false,
            loadedGetSomething:true,
            something : action.data
          });
        case GET_SOMETHING_FROM_SERVER_FAIL:
          return Object.assign({}, state, {
            loadingGetSomething: false,
            loadGetSomethingError: action.data
          });
        default:
          return state;
      }
    
    };
    
    // ACTIONS
    
    /* ----------------- GET SOMETHING ACTIONS START ----------------- */
    import Fetcher from 'isomorphic-fetch'; // superagent , axios libs are okay also 
    
    export function getSomething() {
      return {
        type : GET_SOMETHING_FROM_SERVER
      }
    };
    export function getSomethingSuccess(data) {
      return {
        type : GET_SOMETHING_FROM_SERVER_SUCCESS,
        data
      }
    };
    export function getSomethingFail(data) {
      return {
        type : GET_SOMETHING_FROM_SERVER_FAIL,
        data
      }
    };
    export function getSomethingAsync(paramsToBeSentFromComponents){
      return function(dispatch) {
        const fetcher = new Fetcher();
    
        dispatch(getSomething()); // so we can show a loading gif 
    
        fetcher
          .fetch('/api/views', {
            method : 'POST',
            data : {
              // use paramsToBeSentFromClient
            }
          })
          .then((response) => {
            dispatch( getSomethingSuccess(response.data));
          })
          .catch((error) => {
            return dispatch(getSomethingFail({
              error : error        
            }))
          });
      }
    }
    /* ----------------- GET SOMETHING ACTIONS END ----------------- */
    
    
    // COMPONENT
    
    import React, {Component}       from 'react';
    import { connect }              from 'react-redux';
    import { bindActionCreators }   from 'redux';
    import * as somethignActions    from './redux/something';
    
    @connect((state) => ({
      pathname : state.routing.locationBeforeTransitions.pathname,
      something : state.something
    }))
    
    export default class SettingsMain extends Component{
    
      constructor(props){
        super(props);
        this.somethingActions = bindActionCreators(somethingActions, this.props.dispatch);
      }
    
      componentDidMount(){
        // you are free to call your async function whenever 
        this.settingActions.getSomething({ this.props.pathname...... })
      }
    
      render(){
        return ( /* your components */ )
      }
    }