Sai
Sai

Reputation: 841

How do I update URL parameters and call fetch while on the same page with React?

A little context, I am trying to build a small property site.

On my home component/page, I have a form component. When inputs are completed and submit is clicked, the page is pushed to the list page with the inputs attached to the URL using

this.props.history.push(`/listing/${sleeps}/${startDate}/${endDate}`);

On the listing component/page, a fetch request is made using data from (this.props.match.params) which then updates state with details from fetch. The state is then passed on as props to a map component which creates markers and a list component which creates a list of properties.

On the listing component/page, I have included the form component for users to update their search, however, when submit is clicked, the URLs update but match.params don't, so fetch doesn't run.

How can I update the params and call fetch with the new data whilst staying on the same page?

///FORM COMPONENT///

import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';

class Form extends Component {
    constructor(props) {
        super(props);

        this.state = {
            sleeps: "",
            startDate: "",
            endDate: ""
        }
    }

    render () {
        const {sleeps, startDate, endDate} = this.state;

        return (
            <div>
                <form 
                    name="myForm"
                    autoComplete="off"
                    onSubmit = {(e) => {
                        e.preventDefault();
                        this.props.history.push(`/listing/${sleeps}/${startDate}/${endDate}`);
                    }}>
                    <input 
                        name="sleeps"
                        type="text"
                        required
                        value={sleeps}
                        onChange = {(e) => {
                            this.setState({sleeps: e.target.value});
                        }}
                    />
                    <input 
                        name="startdate"
                        type="text"
                        required
                        placeholder="yyyy-mm-dd"
                        value={startDate}
                        onChange = {(e) => {
                            this.setState({startDate: e.target.value});
                        }}
                        pattern="(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))"
                    />
                    <input 
                        name="enddate"
                        type="text"
                        required
                        placeholder="yyyy-mm-dd"
                        value={endDate}
                        onChange = {(e) => {
                            this.setState({endDate: e.target.value});
                        }}
                        pattern="(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))"
                    />
                    <button
                        type="submit" 
                        id="submit">
                        click
                    </button>
                </form>
            </div>
        )
    }
}

export default withRouter(Form);

///LISTING COMPONENT///

import React, {Component} from 'react';
import MapList from './MapList';
import List from './List';
import Form from './Form';

class Listing extends Component {
    _isMounted = false;

    constructor(props) {
        super(props);

        this.state = {
            mapLatLng: [],
            listInfo: []
        }
    }

    getData = () => {
        const {sleeps, start, end} = this.props.match.params;
        const params = `sleeps=${sleeps}&startdate=${start}&enddate=${end}`

        fetch(url + params)
        .then(response => {
            if (response.ok) {
                return response.text()
            }
        })
        .then(xml => {
            return new DOMParser().parseFromString(xml, "application/xml")
        })
        .then(data => {
            //...data

            if (this._isMounted) this.setState({mapLatLng:mapData, listInfo:listData});
        })
        .catch(err => {
            console.log(err);
        });
    }

    componentDidMount = () => {
        this._isMounted = true;
        this.getData();
    }

    componentWillUnmount = () => this._isMounted = false;

    render () {
        return (
            <div>
                <Form />

                <List data={this.state.listInfo} />

                <div className="map-container">
                    <MapList data={this.state.mapLatLng} />
                </div>
            </div>
        )
    }
}

export default Listing;

///EDIT///INCLUDING APP/ROUTER

import React, {Component} from 'react';
import Listing from './Listing';
import Single from './Single';
import Home from './Home';
import {Switch, Route, Link} from 'react-router-dom';

import './App.css';

class App extends Component {
    render () {
        return (
            <div>
                <Link to="/listing"><p>listings</p></Link>
                <Link to="/"><p>home</p></Link>
                <Switch> 
                    <Route path="/" exact component={Home} />
                    <Route path="/listing/:sleeps/:start/:end" component={Listing} />
                    <Route path="/:id" component={Single} />
                </Switch>
            </div>
        )
    }
}

export default (App);

Upvotes: 0

Views: 1567

Answers (3)

Georgiy T.
Georgiy T.

Reputation: 1211

You should use componentDidUpdate to get a new data according to your router config. Listing component:

componentDidUpdate(prevProps) {
  if (prevProps.match.params !== this.props.match.params) {
    this.getData();
  }
}

You've got a loop in componentDidUpdate since you try to get a data every time when the component updates.

Upvotes: 1

Rahi
Rahi

Reputation: 324

//In your listing component you can pass the getData function in your form component
 constructor(props) {
        super(props);

        this.state = {
            mapLatLng: [],
            listInfo: []
        }
        
        this.getData = this.getData.bind();
}

render () {
        return (
            <div>
                <Form getData={this.getData} />

                <List data={this.state.listInfo} />

                <div className="map-container">
                    <MapList data={this.state.mapLatLng} />
                </div>
            </div>
        )
    }
    
    
  // Then in your form comppnent you can call get data on submit
     <form 
                    name="myForm"
                    autoComplete="off"
                    onSubmit = {(e) => {
                        e.preventDefault();
                        this.props.history.push(`/listing/${sleeps}/${startDate}/${endDate}`);
                        this.props.getData();
                    }}>

Upvotes: 1

fortysixandtwo
fortysixandtwo

Reputation: 485

From what I understand, you will have to run getData again in componentDidUpdate in your listing component (Docs) when your URL props change.

Upvotes: 1

Related Questions