React-router: how to wait for an asynchronous action before a route transition

Is it possible to invoke the async redux action known as thunk on a particular route and not jump until the response works or works?

Use case

We need to download data from the server and fill out the form with the initial values. These initial values ​​do not exist until data is received from the server.

some syntax like this would be great:

 <Route path="/myForm" component={App} async={dispatch(loadInitialFormValues(formId))}> 
+6
source share
2 answers

To answer the original question about preventing a switch to a new route until the answer works or works:

Because you use shortening, you can succeed or fail in the creator of the redirect initiating action. I don't know what your specific action / action creator looks like, but something like this might work:

 import { browserHistory } from 'react-router' export function loadInitialFormValues(formId) { return function(dispatch) { // hit the API with some function and return a promise: loadInitialValuesReturnPromise(formId) .then(response => { // If request is good update state with fetched data dispatch({ type: UPDATE_FORM_STATE, payload: response }); // - redirect to the your form browserHistory.push('/myForm'); }) .catch(() => { // If request is bad... // do whatever you want here, or redirect browserHistory.push('/myForm') }); } } 

Following actions. General data loading scheme when entering a route / on a component; Component display and counter display:

From redux docs on asynchronous actions http://redux.js.org/docs/advanced/AsyncActions.html

  • An action that informs the reducers that the request has started.

Gearboxes can handle this by switching the isFetching flag to state. Thus, the user interface knows its time to display the counter.

  • An action that tells reducers that the request completed successfully.

Gearboxes can handle this by combining the new data into the state it is in control of and resetting isFetching. The user interface will hide the spinner and display the extracted data.

  • An action that tells reducers that the request has failed.

Gearboxes can handle this by dropping isFetching. In addition, some gearboxes may want to save an error message so that the user interface can display it.

I followed this general outline below, using your situation as a rough guide. You do not need to use promises

 // action creator: export function fetchFormData(formId) { return dispatch => { // an action to signal the beginning of your request // this is what eventually triggers the displaying of the spinner dispatch({ type: FETCH_FORM_DATA_REQUEST }) // (axios is just a promise based HTTP library) axios.get(`/formdata/${formId}`) .then(formData => { // on successful fetch, update your state with the new form data // you can also turn these into their own action creators and dispatch the invoked function instead dispatch({ type: actions.FETCH_FORM_DATA_SUCCESS, payload: formData }) }) .catch(error => { // on error, do whatever is best for your use case dispatch({ type: actions.FETCH_FORM_DATA_ERROR, payload: error }) }) } } // reducer const INITIAL_STATE = { formData: {}, error: {}, fetching: false } export default function(state = INITIAL_STATE, action) { switch(action.type) { case FETCH_FORM_DATA_REQUEST: // when dispatch the 'request' action, toggle fetching to true return Object.assign({}, state, { fetching: true }) case FETCH_FORM_DATA_SUCCESS: return Object.assign({}, state, { fetching: false, formData: action.payload }) case FETCH_FORM_DATA_ERROR: return Object.assign({}, state, { fetching: false, error: action.payload }) } } // route can look something like this to access the formId in the URL if you want // I use this URL param in the component below but you can access this ID anyway you want: <Route path="/myForm/:formId" component={SomeForm} /> // form component class SomeForm extends Component { componentWillMount() { // get formId from route params const formId = this.props.params.formId this.props.fetchFormData(formId) } // in render just check if the fetching process is happening to know when to display the spinner // this could also be abstracted out into another method and run like so: {this.showFormOrSpinner.call(this)} render() { return ( <div className="some-form"> {this.props.fetching ? <img src="./assets/spinner.gif" alt="loading spinner" /> : <FormComponent formData={this.props.formData} /> } </div> ) } } function mapStateToProps(state) { return { fetching: state.form.fetching, formData: state.form.formData, error: state.form.error } } export default connect(mapStateToProps, { fetchFormData })(SomeForm) 
+8
source

First of all , I want to say that there - this discussion around the topic of receiving data using response-router onEnter catch on if this is really good practice, however, it is somehow like this:

You can transfer the reduction shop to the Router . Let there be your root component where the Router installed:

 ... import routes from 'routes-location'; class Root extends React.Component { render() { const { store, history } = this.props; return ( <Provider store={store}> <Router history={history}> { routes(store) } </Router> </Provider> ); } } ... 

And your routes will look something like this:

 import ... ... const fetchData = (store) => { return (nextState, transition, callback) => { const { dispatch, getState } = store; const { loaded } = getState().myCoolReduxStore; // loaded is a key from my store that I put true when data has loaded if (!loaded) { // no data, dispatch action to get it dispatch(getDataAction()) .then((data) => { callback(); }) .catch((error) => { // maybe it failed because of 403 forbitten, we can use tranition to redirect. // what in state will come as props to the component `/forbitten` will mount. transition({ pathname: '/forbitten', state: { error: error } }); callback(); }); } else { // we already have the data loaded, let router continue its transition to the route callback(); } } }; export default (store) => { return ( <Route path="/" component={App}> <Route path="myPage" name="My Page" component={MyPage} onEnter={fetchData(store)} /> <Route path="forbitten" name="403" component={PageForbitten} /> <Route path="*" name="404" component={PageNotFound} /> </Route> ); }; 

Note that your router file exports thunk with your repository as an argument, if you look up, look at how we call the router, we pass it the store object.

Unfortunately, at the time of writing, the reaction router returned me 404, so I can not tell you the documents where (nextState, transition, callback) described. But, about them, from my memory:

  • nextState describes how the react-router route will go to:

  • transition function to transform, possibly another transition than one of nextState ;

  • callback will complete the transition on the route.

Another point to note is that with redux-thunk, your submit action can return a promise, check it in the docs here . You can find a good example here of how to configure reduct storage using decux-thunk.

+2
source

All Articles