Redirecting on Button press React Router 4 using async

I’ve read all kinds of help with this issue, and finally am using withRouter as follows to access the history:

index.js

import { withRouter } from 'react-router'
const AppWithRouter = withRouter(App)

ReactDOM.render(
  <Router>
    <AppWithRouter />
  </Router>,
  document.getElementById('root'),
)

Now i can access my history object anywhere in my application, but when I try to redirect after creating a new resource, it redirects before my state is updated, and I get the equivalent of my 404 page. If I refresh with the browser button immediately, I see the new resource. this is my code. I’m not using async correction. Can anyone suggest a change?

Here is the button code in a child component:

<button
              type="button"
              className="ui basic button green"
              style={{ alignSelf: 'flex-end', marginRight: 0 }}
              onClick={this.props.addResource}
            >
              SAVE
            </button>

Here is the addResource called on click above.

App.js

  addResource() {
  console.log(this.state.resource)
  const slug = this.state.resource.friendly
  const redirectURL = `/topics/${slug}`
  const newResource = apiCalls.createResource(this.state.resource)
    const makeRedirect = async () => {
      this.setState({resources: [...this.state.resources, newResource]})
      return "done"
    }
    makeRedirect()
    this.props.history.push(redirectURL)

}

I can link my github repo if anyone is interested as well.

@JM-Mendez tagging you here to save room in your inbox, and to see if anyone else has an idea.

You are using async incorrectly, but that is not causing your bug.

this.setState is because it’s asynchronous, so you can’t depend on changes being completed to calculate your next dom change

https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

And here you’re expecting makeRedirect to set the state, and then push the history

const makeRedirect = async () => {
      this.setState({resources: [...this.state.resources, newResource]})
      return "done"
}

// this may not complete synchronously
makeRedirect()
// so there's a chance this will finish first
this.props.history.push(redirectURL)

Instead, you’ll have to redirect on componentDidUpdate

https://reactjs.org/docs/react-component.html#componentdidupdate

You shouldn’t see any FOUC (Flash Of Unstyled Content) I don’t think. React won’t paint to the DOM until all components have finished rendering. So you can think of your lifecycle methods as looping through your logic until it achieves a stable state.

You’ll really only see FUOC if you attempt to change the DOM during an async process, since that breaks the synchronous nature of javascript.

1 Like

Hi, I was on vacation for awhile and hard getting back into things.

I was able to play with this and I can get it to redirect with component did update, but I can’t get it to do it selectively, ie waiting until I add a new topic to redirect to that topic to view. There are other ways around this (I think), like requering the database, but like I had asked a month or so ago, there is a pattern where the new document is added to state as well as to the DB, but the DB is not required.

I build the redirect URL as the document is saved to the database. but otherwise I don’t need to redirect, so the having component did update redirect on every load gives me an endless loop.

You have to wrap calling setState in a condition. See the bolded section at the end of this image

ahh, ok cool.

Except now my local instance isn’t loading the database and I can’t sort out why. not sure if it’s a docker issue or something else. too much time off, I can’t recall what I had messed with. I do have versioning but not sure how far to go back.

Did you turn on the logs? If so, what does it say? And if your repo is up to date, I could take a look if you want.

Now is also a good time to start thinking about adding tests to your codebase. It will help spot regression errors like this when you start moving stuff around.

the repo is up to date!

Can you give me a hint ( I think you already did and I’ll go look again at ou rmessages) about addign tests?

I think the logs are turned on already? I don’t think I turned them off.

“critical error fetching resources: +0ms TypeError: Failed to fetch” is in the console from api.js

I’ll try to clone it tomorrow and add some quick tests. At least for the database and server portion that I updated for you. You’ll have to look into using either enzyme or cosmos to test your react components tho.

This error looks to be a malformed url. If you look at the try block I linked to below, I logged the provided url. Look for this line in the logs for api.js.

log.info('attempting to fetch resources from:', API_URL).

The error is from the log on line 49.

Oh right, i took that for granted, it is looking for this:
http://localhost:4000/api/resources/
which should be correct! I couldn’t find any changes I had made. Maybe I’ll reboot my machine, that’s always an option, right?

Ok, I’ll take a look tomorrow. Can you send me your non-secret env vars? I’ll have to replicate your environment as closely as possible.

1 Like