Beginner React/JS question -
In my React app, I have a search bar contained in the 'Navbar' component, and a 'FilmTable' component that displays the results of a fetch request in a table on the homepage. On the initial page render (componentDidMount), the FilmTable component returns all the films in my DB (http:/url/films). I need it to re-render the FilmTable component with a new URL for the fetch request (eg. http:/url/films?title=somefilm), based on the input of the search bar in the navbar component.
I'm a bit lost on how to pass the data given in the search bar to the other component and re-render it with the new URL (as derived from the search value).
FilmTable.js component:
import React from "react";
class FilmTable extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
films: []
};
}
componentDidMount() {
fetch("http://url/films/")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
films: result.films
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
NavBar.js component:
class Navbar extends React.Component {
render() {
return (
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<a className="navbar-brand mx-3" href="index.html">Internet Film Database</a>
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<a href="#" className="btn btn-dark mx-1" id="insert-button">Insert a new film</a>
<a href="#" className="btn btn-dark mx-1" id="update-button">Update film</a>
<a href="#" className="btn btn-danger mx-1" id="insert-button">Delete film</a>
</li>
</ul>
<div className="w-25 ms-auto pr-4 mx-3">
<form className="form-inline mx-2 mx-lg-0 input-group">
<input className="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search"></input>
<button className="btn btn-outline-success mx-2 mx-sm-0" type="submit">Search</button>
</form>
</div>
</nav>)
}
}
Index.js
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<div>
<Navbar />
<FilmTable />
</div>
);
Thanks for reading
CodePudding user response:
Create a Parent Component: Home.js
Home.js
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
films: [],
searchParam: ""
};
}
render() {
return (
<>
<Navbar searchParam={searchParam}/>
<FilmTable films={this.state.films}/>
</>
)
}
}
On submitting searchParams, fetch the data from api and update the films props which will then update the FilmTable Component.
CodePudding user response:
You have to lift the state (React docs). Since data only flows from parent to child in React, and NavBar and FilmTable are just siblings, you need to wrap them in some parent that stores the search term. You would then pass down the setter function and state value as props to the two child components.
class App extends React.Component {
constructor() {
super();
this.state = {
searchTerm: ""
};
}
handleSearchChange(searchString) {
this.setState({ searchTerm: searchString });
}
render() {
return (
<div>
<Navbar handleSearchChange={this.handleSearchChange}/>
<FilmTable searchTerm={this.searchTerm}/>
</div>
);
}
}
// In NavBar, the onSubmit or onChange triggers will now call
// the function you passed down as props.
<form onSubmit={e => this.props.handleSearchChange(e.target.value)} >
</form>
// In FilmTable:
// Since the NavBar changes the state on the parent,
// and FilmTable is a child of parent, it will
// re-remount with the passed in searchTerm prop.
fetch(`http://url/films?title=${this.props.searchTerm}`)
It's also convention to use "Controlled Components" in React, so you might want to turn the form into one. If you do, make sure to use props.searchTerm and props.handleSearchChange instead of adding local state to the NavBar.