DEV Community

Ekim Kael
Ekim Kael

Posted on

React, React-Router and Fetch API

I'm having a problem with my React.

I use React-Router and the Fetch API.

So... I have a list of products and when I click on one, I am redirected to a route to display more details on the product in question but also to display the other products.

Say: reactapp.com/all for all products

reactapp.com/p/productID my two URLs

My problem:

When I am on reactapp.com/p/productID and I click on another product, the URL is updated but the product information is not updated.

When I click a second time, the product information I previously clicked is displayed
I don't know if you understand me.I have a data restriction at the moment.so I can't deploy so you can see it.
Please if you've understood what i mean, help me

Top comments (9)

Collapse
 
sirmoustache profile image
SirMoustache

Hi!
I had a similar problem, a solution that helped me - is to wrap component with withRouter HOC from react-router-dom

like this:

import { withRouter } from 'react-router-dom';

class MyAwesomeComponent extends React.Component {
  // ...
}

export default withRouter(
  MyAwesomeComponent 
);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ekimkael profile image
Ekim Kael • Edited

it didn't work.
There is some styledComponent code that i remove
Here is my code:


export default class Work extends Component {
  constructor(props) {
    super(props)
    const { match: { params } } = this.props

    this.state = { 
      url: params.work, 
      loading: true, 
      work: [],
      otherWorks: []
    }
  }

  handleClick = () => {
    const { url, loading } = this.state 
    this.setState({
      loading: !loading,
      url
    })

    fetch(`/w/${url}`)
      .then(res => res.json())
      .then(work => this.setState({ work: work }))
      .then(fetch(`/works?&_limit=6&id_ne=${url}`)
          .then(res => res.json())
          .then(others =>
            this.setState({ otherWorks: others, loading: false, url: this.props.match.params.work })))
  }



  componentDidMount = () => {
    this.handleClick()
  }

  render() {    
    console.log(this.state)
    const { loading, work, otherWorks } = this.state 

    if (loading) {
      return <Loader />
    } else {
    return <WorkWidget>
        <Cover>
          <Carousel />
          <Details>
          <Title>{work.name}</Title>
            <Paragraph>{work.desc}</Paragraph>
            <Row>
              <Section>
                <Subtitle>Client</Subtitle>
                <Info>{work.client}</Info>
              </Section>
              <Section>
                <Subtitle>Role</Subtitle>
                <Info>Fullstack</Info>
              </Section>
              <Section>
                <Subtitle>Url</Subtitle>
                <InfoUrl href={work.url} target="_blank" rel="noopener noreferrer">Visit</InfoUrl>
              </Section>
            </Row>
          </Details>
        </Cover>
        <OtherWorks>
          {otherWorks.map(otherWork => [
          <Link key={otherWork.id} to={`/w/${otherWork.id}`} onClick={this.handleClick}>
              <Works>
                <section>
                  <AppName>{otherWork.name}</AppName>
                  <Category>
                    {otherWork.category}
                  </Category>
                </section>
              </Works>
            </Link>
          ])}
        </OtherWorks>
      </WorkWidget>
    }
  }
}
Collapse
 
dance2die profile image
Sung M. Kim • Edited

Hi @iamkael .

Call fetch as a callback of this.setState().

this.setState({
    loading: !loading,
    url
}, () => fetch(`/w/${url}`).<rest of the chain>...)

My guess is that, handleClick's first this.setState sets the loading property, which is not properly recognized as this.setState is an asynchronous operation.

You'd fetch after loading property is turned off if you want such a synchronous behavior.

Collapse
 
nqbinh17 profile image
nqbinh17

Hey, I got the same problem with you. If you use HashRouter and click on its siblings, React only changes params in your Class Work, so componentDidMount will not be called again => webpage is not updated! You can add componentDidUpdate to fix this, or put this.handleClick() directly in Render() ( because React will invoke this as you click on another product).

Collapse
 
sirmoustache profile image
SirMoustache

There is another solution with passing location property to your component.

You can check it here here

Collapse
 
deepakr28 profile image
Deepak Rawat

hello Ekim, Great read however i have a doubt
I don't know much about security apart from env variables when it comes to storing API keys.

my question is regarding making API calls from ReactJS, is it a good approach even if I store the API keys in the env variables? I read a few articles which said anything passed to the front-end is no longer secure. So how bad is it, making API calls from ReactJS?

and what are alternate solutions? making custom backend with express or something else, and doing all that stuff there?

Collapse
 
ekimkael profile image
Ekim Kael

It really depends on what you have to do deal with.
API are also made to be a bit stronger as all we send in Front can be seen.
Making a custom Backend for me is way to complicated, I use to go with Nextjs, it offer the possibility to have an hybrid app if I can say. The parts you to be handle server side can(mostly when user is authenticated) and also static parts.

Don't know if I answered your question but:
1 - Use env. variables
2 - If you want the confort of React but have to deal with sensitive datas and want them to be serve by backend try Nextjs

Collapse
 
deepakr28 profile image
Deepak Rawat

thanks for replying Ekim, can you tell me more about hybrid app, since i am also using next.js, or can you point me to some resources, it'll be highly appreciated.

Thread Thread
 
ekimkael profile image
Ekim Kael

What are you trying to do?
Maybe that way I can help you more efficiently.