ReactJS - passing data from a child component to its parent - javascript

I have the following structure of components in the application:
class Car extends Component {
constructor() {
super();
this.state = {
cars: [],
...
}
}
componentDidMount() {
axios.get('/api/cars')
.then((response) => {
this.setState({cars: response.data});
console.log('cars: ', cars);
}).catch(err => {
console.log('CAUGHT IT! -> ', err);
});
}
render() {
return (
...
<CarAddNew />
<CarSearch />
<CarList cars={this.state.cars} />
)
}
}
and then
export default class CarSearch extends Component {
constructor(){...}
handleSearchSubmit(e) {
e.preventDefault();
..
axios.post('/api/cars/search', searchCars)
.then(response => {
console.log('response.data: ', response.data);
})
}
render() {
return(
... search form ...
)
}
When I search data in the database through the CarSearch component, it will fetch and load the right data, that's great. However, how do I pass this "new" found data to the CarList component, so I can display the on the page?

What I would do is the following:
class Car extends Component {
constructor() {
super();
this.state = {
cars: [],
...
}
}
componentDidMount() {
axios.get('/api/cars')
.then((response) => {
this.setState({cars: response.data});
console.log('cars: ', cars);
}).catch(err => {
console.log('CAUGHT IT! -> ', err);
});
}
handleSearch = () => {
axios.post('/api/cars/search', searchCars) // not sure where you are getting searchCars from, but you should get the idea
.then(response => {
this.setState({cars: response.data})
console.log('response.data: ', response.data);
})
}
render() {
return (
...
<CarAddNew />
<CarSearch onSearch={this.handleSearch} />
<CarList cars={this.state.cars} />
)
}
}
export default class CarSearch extends Component {
constructor(){...}
handleSearchSubmit(e) {
e.preventDefault();
this.props.onSearch() // I'm assuming you probably want to pass something here
}
render() {
return(
... search form ...
)
}

One option is to propagate the data up through a prop on CarSearch. Consider the (truncated) example...
handleSearchSubmit(e) {
e.preventDefault();
axios.post('/api/cars/search', searchCars).then(response => {
this.props.onData(response.data);
});
}
where, onData calls back up to the following (then later setting state)...
constructor() {
// [...]
this.onSearchResult = this.onSearchResult.bind(this);
}
onSearchResult(cars) {
this.setState({cars}); // results from CarSearch
}
render() {
return (
<CarAddNew />
<CarSearch
onData={this.onSearchResult} />
<CarList
cars={this.state.cars} />
)
}

Related

Correct way to fetch through array

In the below compoenent, the function is neverending. Can someone tell me what to fix so that in the end the beers array in the state has 5 names?
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
didError: false
};
this.getBeerInfo = this.getBeerInfo.bind(this);
}
render() {
return (
...
}
getBeerInfo() {
let beerArr = [1,2,3,4,5];
this.props.beerArr.map(id => {
fetch(`https://api.punkapi.com/v2/beers/${id}`)
.then(res => res.json())
.then(json => {
this.setState(state => {
const beers = state.beers.concat(json[0].name);
return {
beers
};
});
})
.catch(err => {
this.setState({
didError : true
});
});
})
}
}
Well your code should be somethings like this ..
import React from 'react';
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
didError: false
};
this.getBeerInfo = this.getBeerInfo.bind(this);
}
render() {
return (
<div>{this.state.beers}</div>
)
}
componentDidMount() {
this.getBeerInfo()
}
getBeerInfo() {
let beerArr = [1,2,3,4,5];
beerArr.map(id => {
fetch(`https://api.punkapi.com/v2/beers/${id}`)
.then(res => res.json())
.then(json => {
this.setState({
//const beers = state.beers.concat(json[0].name);
//return {
//beers
//};
beers: this.state.beers.concat(json[0].name)
});
console.log('well at least this works')
})
.catch(err => {
this.setState({
didError : true
});
});
})
}
}
It is advised that you use the componentDidMount() lifecycle method for the fetch api and add what #atahnksy said.
When you are using setState, you can try this:
this.setState({ beers: [...this.state.beers, json[0].name])
This might fix your problem.
You can improve the render method using a combination of ternary operator(to display appropriate message when it cannot reach the server), format with map and ordered list to get something like this :
render() {
return (
<div><ol>{this.state.beers.length!==0 ? this.state.beers.map((beer)=><li>{beer}</li>) :"Could not retrieve any bears. Try again/ensure you can access the server/networtk"}</ol></div>
)
}

Can't set state data

I am new to react programming. It might be silly mistake but, i can't access state data in my smart component.
Following is my code.
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state = {
resData: [],
}
}
componentDidMount() {
fetch(`http://someurl.com/something`)
.then(response => response.json())
.then(result => { alert(result.data[0].title); this.setState({ resData: result.data }));
}
render() {
return (
<div>
<Header />
<ErrorBoundary>
<Content data={ this.state.resData } />
</ErrorBoundary>
<Footer />
</div>
);
}
export default App;
If i alert data in following then it was there.
.then(result => { alert(result.data[0].title) setState({ resData: result.data })); //Here i can see my data.
I want to pass this state data to my component. But, there are no data.
<Content data={ this.state.resData } />
Any help would be greatly appreciated.
Try now:
You need to use this keyword with setState()
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state = {
resData: [],
}
}
componentDidMount() {
fetch(`http://someurl.com/something`)
.then(function (response) {
return response.json()
})
.then(function (result) {
this.setState({ resData: result.data })
})
.catch(function (error) {
alert("Username password do not match")
})
}
render() {
const { resData } = this.state;
return (
<div>
{resData &&
<Header />
<ErrorBoundary>
<Content data={resData} />
</ErrorBoundary>
<Footer />
}
</div>
);
}
export default App;
Check it now
The alert is running before the setState is finishing, try running the alert as a callback to setState:
componentDidMount() {
fetch(`http://someurl.com/something`)
.then(response => response.json())
.then(result => this.setState({ resData: result.data }), () => {
alert(this.state.resData);
});
}
try this it might help
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state = {
resData: [],
}
}
componentDidMount() {
var that = this;
fetch(`http://someurl.com/something`)
.then(response => response.json())
.then(result => { alert(result.data[0].title); that.setState({ resData: result.data }));
alert(that.state.resData);
}
render() {
var that = this;
return (
<div>
<Header />
<ErrorBoundary>
<Content data={ that.state.resData } />
</ErrorBoundary>
<Footer />
</div>
);
}
export default App;

Automatically render child component when state has been updated in parent component

The parent component Dashboard holds the state for every ListItem I add to my Watchlist. Unfortunately, every time I am adding an Item, it gets added to the DB, but only shows up when I refresh the browser.
class UserDashboard extends React.Component {
state = {
data: []
}
componentWillMount() {
authService.checkAuthentication(this.props);
}
isLoggedIn = () => {
return authService.authenticated()
}
getAllCoins = () => {
//fetches from backend API
}
addWishlist = () => {
this.getAllCoins()
.then(things => {
this.setState({
data: things
})
})
console.log("CHILD WAS CLICKED")
}
componentDidMount() {
this.getAllCoins()
.then(things => {
this.setState({
data: things
})
})
}
render() {
return (
<div className="dashboard">
<h1>HI, WELCOME TO USER DASHBOARD</h1>
<SearchBar
addWishlist={this.addWishlist}
/>
<UserWatchlist
data={this.state.data}
/>
</div>
);
}
}
The User Watchlist:
class UserWatchlist extends React.Component {
constructor(props) {
super(props)
}
// componentDidUpdate(prevProps) {
// if (this.props.data !== prevProps.data) {
// console.log("CURRENT", this.props.data)
// console.log("PREVs", prevProps.data)
// }
// }
render() {
return (
<div>
<h2>These are tssssyou are watching:</h2>
<ul className="coin-watchlist">
{
this.props.data.map((coin, idx) => {
return <ListItem key={idx}
coin={coin.ticker}
price={coin.price}
/>
})
}
</ul>
</div>
)
}
}
The search Bar that shows potential Items to watch over:
class SearchBar extends React.Component {
constructor(props) {
super(props)
this.state = {
coins: [],
searchValue: ""
}
}
searchHandler = e => {
e.preventDefault()
const value = e.target.value
this.setState({
searchValue: value
});
if (value === "") {
this.setState({
coins: []
})
} else {
this.getInfo()
}
}
getInfo = () => {
// Searches the API
}
addWishlist = () => {
this.props.addWishlist();
}
render() {
const {coins, searchValue} = this.state
return (
<div className="coin-search">
<form>
<input
type="text"
className="prompt"
placeholder="Search by ticker symbol"
value={searchValue}
onChange={this.searchHandler}
/>
</form>
<ul className="search-suggestions">
{
coins.filter(searchingFor(searchValue)).map( coin =>
<Currency
coin={coin}
addWishlist={this.addWishlist}
/>
)
}
</ul>
</div>
);
}
}
And the actual Currency that gets clicked to be added:
class Currency extends React.Component {
addToWatchlist = () => {
// POST to backend DB to save
};
fetch("/api/add-coin", settings)
.catch(err => {
return err
})
}
clickHandler = () => {
this.addToWatchlist()
this.props.addWishlist()
}
render() {
return(
<div className="search-results">
<li>
<h3> { this.props.coin.currency } </h3>
<button
className="add-to-list"
onClick={this.clickHandler}
>
+ to Watchlist
</button>
</li>
</div>
)
}
}
As you can see, I am sending props down all the way down to child. When I click the button to Add to Watchlist, I see the console.log message appear, saying "CHILD WAS CLICKED". I've even tried just calling the method to fetch from backend API again.
Also, in UserWatchlist, I've tried a componentDidUpdate, but both prevProps and this.props show the very same array of data. Somewhere in the chain, my data is getting lost.
This is also my first time posting a question here, so if it can be improved, I am happy to add extra details and contribute something to this community
You probably forgot to wait for addToWatchlist to complete:
addToWatchlist = () => {
// POST to backend DB to save
return fetch("/api/add-coin", settings)
.catch(err => {
return err
})
}
clickHandler = () => {
this.addToWatchlist().then(() => {
this.props.addWishlist()
})
}

React - onChange function 'this.state' is undefined

I'm experimenting with React and I'm trying to create a Search to filter a list of items. I have two components, the main one displaying the list of items which calls the Search component.
I have an onChange function that sets the term in the state as the input value and then calls searchItems from the main component to filter the list of items. For some reason in searchItems, this.state is undefined. I thought adding bind to onInputChange in the Search component would sort it out but it did not make any difference. Maybe there's something I'm missing.
Main Component
import React, { Component } from 'react';
import _ from 'lodash';
import Search from './search';
class Items extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
componentDidMount() {
fetch("[url].json")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
}
),
(error) => {
this.setState({
isLoaded: true,
error
})
}
}
searchItems(term) {
const { items } = this.state;
const filtered = _.filter(items, function(item) {
return item.Name.indexOf(term) > -1;
});
this.setState({ items: filtered });
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
}
else if (!isLoaded) {
return <div>Loading...</div>;
}
else {
return (
<div>
<Search onSearch={this.searchItems}/>
<ul>
{items.map(item => (
<li key={item.GameId}>
{item.Name}
</li>
))}
</ul>
</div>
)
}
}
}
export default Items;
Search Component
import React, { Component } from 'react';
class Search extends Component {
constructor(props) {
super(props);
this.state = {
term: ''
};
}
render() {
return (
<div>
<input type="text" placeholder="Search" value={this.state.term} onChange={event => this.onInputChange(event.target.value)} />
</div>
);
}
onInputChange(term) {
this.setState({ term });
this.props.onSearch(term);
}
}
export default Search;
You didn't bind searchItems() in the Items component.
Try changing it to an arrow function:
searchItems = () => {
// blah
}
or otherwise binding it in the constructor():
constructor() {
// blah
this.searchItems = this.searchItems.bind(this);
}
or when you call it.
You can read more about this here.

Set ReactJS results in a additional div, new component?

This is the code my react component:
class App extends React.Component {
constructor() {
super();
}
update(e) {
axios.get('https://myjsonurl.com', {
params: {
phrase: e.target.value
}
}).then(function (response) {
console.log(response.data);
}).catch(function (err) {
console.log(err);
});
}
render() {
return (
<div>
<input type="text" onChange={this.update} />
</div>
)
}
}
By searching I get correct results in console of my browser. Can someone give me some tipps how can I insert this data in a list?
Is there to create a new component and tell in response to use this component?
Create a state variable and save the response inside that. Call a function from render method that will create the list from that response.
Bind the update method inside constructor.
Like this:
class App extends React.Component {
constructor() {
super();
this.state = {
data: []
}
this.update = this.update.bind(this); //bind the method
}
update(e) {
axios.get('https://myjsonurl.com', {
params: {
phrase: e.target.value
}
}).then((response) => {
this.setState({data: response.data}); //set the response in state
}).catch((err) => {
console.log(err);
});
}
createList(){
if(!this.state.data.length) return null;
return <ul>
{this.state.data.map((el, index) => <li key={index}> {el.KeyName} </li>)}
</ul>
}
render() {
return (
<div>
<input type="text" onChange={this.update} />
{this.createList()}
</div>
)
}
}
Note: Replace el.KeyName with actual key that you want to show, and assign some unique value to key key={index} i used index because don't know the details about the response.

Categories