I am new to react and have read the react docs on how to make an Ajax call. They made it look so simple when they were returning the json information but it doesn't work for me. I am trying to call the json information and set it to this.state.stockSymbol but every time I try to access the information I use typeof and it returns an object. I can see the json information clearly when I console.log it but for some reason it won't update in my getSymbol function. I think it has to to with the async call but I'm not totally understanding it. Can someone point me in the right direction?
Here is my code:
class Stocks extends React.Component {
constructor(props) {
super(props);
this.state = {
userInput: '',
stockSymbol: [],
isLoaded: false
}
}
typeSymbol = (e) => {
this.setState({
userInput: e.target.value.toUpperCase()
}, (e) => {
console.log(this.state.userInput)
})
}
getSymbol = (e) => {
e.preventDefault(),
this.setState({
stockSymbol: this.state.stockSymbol
}, () => {
console.log(typeof this.state.stockSymbol)
console.log(this.state.stockSymbol)
})
}
componentDidMount() {
fetch(`https://finnhub.io/api/v1/stock/symbol?exchange=US&token=${key}`)
.then(res => res.json())
.then(
(results) => {
this.setState({
isLoaded: true,
stockSymbol: results
});
console.log(results)
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { stockSymbol, userInput, results } = this.state
stockSymbol.map((stock, i) => {
if (userInput === this.state.stockSymbol) {
return (
console.log('same'),
<span className="symbol" key={i} onSubmit={this.getSymbol}>
{stock.displaySymbol}
</span>
);
}
})
return (
<div className="enterstock">
<h1 className="title">Enter Stock Symbol</h1>
<span className="symbol">{this.state.userInput}</span>
<form className="inputfields" onSubmit={this.getSymbol}>
<input type="text" className="symfields" name="symbolname" onChange={this.typeSymbol}></input>
<button type="submit" className="btn">Send</button>
</form>
</div >
)
}
}
ReactDOM.render(
<Stocks />,
document.getElementById('root')
)
There are couple of issues but not with fetching data.
What you are trying to do is filter stock symbols but you are comparing userInput with Stock symbol rather than with name of each stock
getSymbol doesn't need to setState as you already have set the state after fetching data.
Here is a sandbox that you can try out which does exactly what you are looking for: https://codesandbox.io/s/twilight-dream-jwe7g?file=/src/index.js
Search "leanne graham" to test out
Most likely the problem is with your render function, the mapped isn't actually being rendered. You have to return a single jsx expression that contains everything.
render() {
const { stockSymbol, userInput, results } = this.state
const symbols = stockSymbol.map((stock, i) => {
// removed your if statement, it didn't make sense
return (
<span className="symbol" key={i} onSubmit={this.getSymbol}>
{stock.displaySymbol}
</span>
);
})
return (
<>
{symbols}
<div className="enterstock">
<h1 className="title">Enter Stock Symbol</h1>
<span className="symbol">{this.state.userInput}</span>
<form className="inputfields" onSubmit={this.getSymbol}>
<input type="text" className="symfields" name="symbolname" onChange={this.typeSymbol}></input>
<button type="submit" className="btn">Send</button>
</form>
</div >
</>
)
}
Related
I am current working on a web application that when you input your search data and click push search, it will retrieve the data from the api. I was hoping someone can help me by showing me how to use the user input in order to map the response based on the user input and render in on the screen. Nothing is rendering
import axios from 'axios'
import React, { Component } from 'react'
import '../App.css'
import GasStationList from './GasStationsList'
class Search extends Component {
constructor(props) {
super(props)
this.state = {
initialStations: [],
stations: [],
search: ''
}
}
componentDidMount(){
axios.get('http://api.eia.gov/series/?api_key=9b0550c7825c207680e9b8bcc661f666&series_id=TOTAL.MGUCUUS.M')
.then(response => {
this.setState({initialStations: response.data});
console.log(response)
}).catch(error =>{
console.log(error)
this.setState({
errorMsg: 'Error Retriving Data'
})
});
}
handleSearchChange = value => {
const stationsFiltered = this.state.initialStations.filter(initialStation => initialStation.name.includes(value));
this.setState({stations: stationsFiltered, search: value});
console.log(this.state.filter)
}
clickHandler = () => {
this.setState({
stations: this.state.stationsFiltered
})
}
render(){
return (
<div>
<div className='search-container'>
<label> Station Search </label>
<input
type='text'
name='search'
value={this.search}
onChange={this.handleSearchChange}
/>
</div>
<button
onClick={() => this.clickHandler()}
className='Search-Button'>
Search
</button>
<GasStationList stations={this.state.stations}/>
</div>
)
}
}
export default Search;
Supposing that stations objects have a property called name to be filtered, you could use filter function every time input value changes. Something like:
handleSearchChange = value => {
const stationsFiltered = this.state.station.filter(stat => stat.name.includes(value));
this.setState({stations: stationsFiltered, search: value});
}
Note that this solutions overrides stations every time you made a search. This is not good (every time you have to re-fetch all the unfiltered stations).
Why don't you store initial stations and then filter them saving search result in another state variable. Something like:
class Search extends Component {
constructor(props) {
super(props)
this.state = {
initialStations: [],
stations: [],
search: ''
}
}
handleSearchChange = value => {
// now we are filtering always initialStations and store data filtered in stations
const stationsFiltered = this.state.initialStations.filter(stat => stat.name.includes(value));
this.setState({stations: stationsFiltered, search: value});
}
clickHandler = () => {
axios.get('http://api.eia.gov/series/?api_key=9b0550c7825c207680e9b8bcc661f666&series_id=TOTAL.MGUCUUS.M')
.then(response => {
this.setState({initialStations: response.data, stations: response.data});
console.log(response)
}).catch(error =>{
console.log(error)
this.setState({
errorMsg: 'Error Retriving Data'
})
});
}
render(){
return (
<div>
<div className='search-container'>
<label> Station Search </label>
<input
type='text'
name='search'
value={this.state.search}
onChange={this.handleSearchChange}
/>
</div>
<button
onClick={() => this.clickHandler()}
className='Search-Button'>
Search
</button>
<GasStationList />
<div key={this.series_id}>this the gas stations{`${this.state.data} ${this.state.series_id}`}
and their Id
</div>
{this.state.stations}
</div>
)
}
}
export default Search;
But there is another problem. You could say "if value is empty, I want to show all the stations". To do that, you should change handleSearchChange in this way:
handleSearchChange = value => {
if (value === '') {
this.setState({stations: initialStations, search: value});
}
else {
const stationsFiltered = this.state.initialStations.filter(stat => stat.name.includes(value));
this.setState({stations: stationsFiltered, search: value});
}
}
EDIT
After you change code I see that you are using this.state.stationsFiltered in clickHandler but stationsFiltered is not on state. From your code I also see that you want a behaviour like that:
I make a search (writing something on input);
Then I click button and stations are shown;
Ok so I suggest something like this:
import axios from 'axios'
import React, { Component } from 'react'
import '../App.css'
import GasStationList from './GasStationsList'
class Search extends Component {
constructor(props) {
super(props)
this.state = {
initialStations: [], //<-- this is the entire list of available stations (from fetch)
stations: [], //<-- this is the array with all the filtered stations
search: ''
}
}
componentDidMount(){
axios.get('http://api.eia.gov/series/?api_key=9b0550c7825c207680e9b8bcc661f666&series_id=TOTAL.MGUCUUS.M')
.then(response => {
this.setState({initialStations: response.data});
console.log(response)
}).catch(error =>{
console.log(error)
this.setState({
errorMsg: 'Error Retriving Data'
})
});
}
handleSearchChange = value => {
// here we just set the seach value with what you typed on input
this.setState({search: value});
}
clickHandler = () => {
// here we filter initialStations on name property using this.state.search
const stationsFiltered = this.state.initialStations.filter(initialStation => initialStation.name.includes(this.state.search));
this.setState({
stations: stationsFiltered
})
}
render(){
return (
<div>
<div className='search-container'>
<label> Station Search </label>
<input
type='text'
name='search'
value={this.state.search} // <-- note: here is this.state.search (in your code is written this.search and is not correct)
onChange={this.handleSearchChange}
/>
</div>
<button
onClick={() => this.clickHandler()}
className='Search-Button'>
Search
</button>
<GasStationList stations={this.state.stations}/>
</div>
)
}
}
export default Search;
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I'm trying to show some tickets info from an api but for some reason maping isnt working
class Tickets extends React.Component {
state = {
tickets: [],
clicked: false,
};
onclicked = () => {
this.setState({ clicked: true });
console.log('helloitsme');
};
componentDidMount = async () => {
try {
const res = await getAllTickets();
this.setState({ tickets: res });
} catch (err) {}
};
render() {
return (
<article>
<section className="container">
{this.state.tickets.map((ticket) => (
<div onClick={this.onclicked} key={ticket._id}>
{this.state.clicked === true && (
<div className="boxinfo">
<h1 className="info">{ticket.date}</h1>
<h2 className="info">{ticket.place}</h2>
<h3 className="info">{ticket.price}</h3>
</div>
)}
</div>
))}
</section>
</article>
);
}
}
export default Tickets;
I googled and tried changing the state array but nothing works. Should I do a fetch to the api?
Add to state only if the response contain some data. Also check if your response from getAllTickets is returning an array or not. If the response is not an array then you are likely to get such errors. Below method works if the response is array and it won't set the state until the response contains some data.
class Tickets extends React.Component {
state = {
tickets: [],
clicked: false,
};
onclicked = () => {
this.setState({ clicked: true });
console.log('helloitsme');
};
componentDidMount = async () => {
try {
const res = await getAllTickets();
if (res) {
this.setState({ tickets: res });
}
} catch (err) {}
};
render() {
return (
<article>
<section className="container">
{this.state.tickets.map((ticket) => (
<div onClick={this.onclicked} key={ticket._id}>
{this.state.clicked === true && (
<div className="boxinfo">
<h1 className="info">{ticket.date}</h1>
<h2 className="info">{ticket.place}</h2>
<h3 className="info">{ticket.price}</h3>
</div>
)}
</div>
))}
</section>
</article>
);
}
}
export default Tickets;
Also check if you response contains some other data which is an array. In such case if you get res.data as an array, following code works for you.
class Tickets extends React.Component {
state = {
tickets: [],
clicked: false,
};
onclicked = () => {
this.setState({ clicked: true });
console.log('helloitsme');
};
componentDidMount = async () => {
try {
const { data } = await getAllTickets();
if (data) {
this.setState({ tickets: data });
}
} catch (err) {}
};
render() {
return (
<article>
<section className="container">
{this.state.tickets.map((ticket) => (
<div onClick={this.onclicked} key={ticket._id}>
{this.state.clicked === true && (
<div className="boxinfo">
<h1 className="info">{ticket.date}</h1>
<h2 className="info">{ticket.place}</h2>
<h3 className="info">{ticket.price}</h3>
</div>
)}
</div>
))}
</section>
</article>
);
}
}
export default Tickets;
I am creating a search bar. The user write his request, and when he press the search button, it's stocked in search, sent via Axios, and return the result at the end. However, I have the good results in my console (the code in the back is ok) but I didn't manage to display the data in my page, because when I add these lines in the return
<ul>
{ this.state.filter.map(book => (
<li key={book.id}>{book.title},{book.author}</li>
))}
</ul>
, I have this error: TypeError: Cannot read property 'map' of null. What can I change/add in my code in order to display correctly the result of my request please? (I post the picture of the network without the map)
import React, {Component} from 'react';
import {
MDBContainer,MDBCol, MDBBtn,
} from 'mdbreact';
import "./acceuil.css";
import axios from 'axios';
class Acceuil extends Component{
constructor(props){
super(props);
this.state = {
search:'',
filter: null,
error:'',
}
this.searchTitle = this.searchTitle.bind(this);
}
change = e => {
this.setState({
[e.target.id]: e.target.value,
});
}
searchTitle = (search) => e => {
console.log(search);
let formData = new FormData();
formData.append("search",search);
const url = "http://localhost:8888/recherche/recherche_titre.php"
axios.post(url, formData)
.then(response => response.data)
.then((data) => {
this.setState({filter: data});
console.log(this.state.filter)
})
setTimeout(() => {
this.setState({
error: '',
});
}, 2000);
e.preventDefault();
}
render() {
const container = {height:530};
return(
<div>
<div className="searchPosition">
<MDBCol>
<div className="active-pink-3 active-pink-4 mb-4">
<input className="form-control" id="search" type="text" placeholder="Search" onChange={this.change} />
</div>
</MDBCol>
</div>
<div>
<MDBBtn onClick={this.searchTitle(this.state.search)} color="dark" size="lg">Search</MDBBtn>
<ul>
{ this.state.filter.map(book => (
<li key={book.id}>{book.title},{book.author}</li>
))}
</ul>
</div>
);
}
}
export default Acceuil;
One of the ways to fix this issue is to set the filler to an array in the constructor.
constructor(props){
super(props);
this.state = {
search:'',
filter: [], // <== THIS CHANGED
error:'',
}
this.searchTitle = this.searchTitle.bind(this);
}
Or you can change the render method to ignore null filter, which I personally prefer since you know whether your request is loaded or not
this.state.filter ? this.state.filter.map(book => ...) : <em>Loading...</em>;
Probably one option is to check for null values before using .map().
Try with using && as the following:
<ul>
{ this.state.filter && this.state.filter.map(book => (
<li key={book.id}>{book.title},{book.author}</li>
))}
</ul>
I hope this helps!
This question has come up quite a few times on here, I know, but none of the solutions have worked for me. So, I'm making a call to the iTunes API with a fetch request, based on user input in a React app. The initial call/submit works just fine. However, when I try to do a second search, I get a this.setState is not a function error as soon as I type in the input box. I've tried binding this in all the methods and in all the ways one can, but no success in getting it to work. Any ideas?
class App extends Component {
constructor(props) {
super(props);
this.state = {
query: '',
artist: null,
albums: []
};
this.handleChange = this.handleChange.bind(this);
this.getSearchResults = this.getSearchResults.bind(this);
}
handleChange(event) {
this.setState({ query: event.target.value });
}
getSearchResults() {
console.log('query!!! ' + this.state.query);
const URL_TEMPLATE = "https://itunes.apple.com/search?entity=album&limit=25&term={query}";
let url = URL_TEMPLATE.replace('{query}', this.state.query);
fetch(url)
.then((response) => {
let data = response.json();
return data;
})
.then((data) => {
console.log(data.results);
this.setState ({
albums: data.results
});
})
.catch((err) => {
console.log(err);
});
this.setState({ query: '' });
}
render() {
return (
<div className="container">
<hr />
<div className="col-lg-6">
<div className="input-group">
<input type="text"
value={this.state.query}
onChange={this.handleChange}
className="form-control" placeholder="Search for..." />
<span className="input-group-btn">
<button
onClick={() => this.getSearchResults()}
className="btn btn-default" type="button">Go
</button>
</span>
</div>
</div>
<hr />
<div>
Albums: {this.state.albums}
</div>
</div>
);
}}
edit: I should mention that it gives me the error on the handleChange() method, since that's what deals with the input.
Here is the complete working code
import React from 'react';
class TestJS extends React.Component {
constructor(props) {
super(props);
this.state = {
query: '',
artist: null,
albums: []
};
this.handleChange = this.handleChange.bind(this);
this.getSearchResults = this.getSearchResults.bind(this);
}
handleChange(event) {
this.setState({ query: event.target.value });
}
getSearchResults() {
console.log('query!!! ' + this.state.query);
const URL_TEMPLATE = "https://itunes.apple.com/search?entity=album&limit=25&term={query}";
let url = URL_TEMPLATE.replace('{query}', this.state.query);
fetch(url)
.then((response) => {
let data = response.json();
return data;
})
.then((data) => {
console.log(data.results);
this.setState({
albums: data.results
});
})
.catch((err) => {
console.log(err);
});
this.setState({ query: '' });
}
render() {
let plus5 = [];
if(!!this.state.albums && this.state.albums.length > 0){
this.state.albums.map((val, i, arr) => {
plus5.push(<div key={i}>{val.artistId}</div>);
});
}
return (
<div className="container">
<hr />
<div className="col-lg-6">
<div className="input-group">
<input type="text"
value={this.state.query}
onChange={(e) => this.handleChange(e)}
className="form-control" placeholder="Search for..." />
<span className="input-group-btn">
<button
onClick={() => this.getSearchResults()}
className="btn btn-default" type="button">Go
</button>
</span>
</div>
</div>
<hr />
<div>
{plus5}
</div>
</div>
);
}
}
export default TestJS;
I've just rendered artistId. You can render anything you want
Thanks!
Does arrow function work?
<input type="text"
value={this.state.query}
onChange={(e) => this.handleChange(e)}
className="form-control" placeholder="Search for..." />
And you cannot call setState() like this:
this.setState = {
albums: data.results
};
please change to :
this.setState({
albums: data.results
});
I think since you are inside the closure, this is not available inside
.then((data) => {
console.log(data.results);
this.setState = {
albums: data.results
};
})
Try assigning let that = this outside and before the fetch call.
And then
.then((data) => {
console.log(data.results);
that.setState = {
albums: data.results
};
})
I'm trying to update some immutable data. However, I am doing it incorrectly and it doesn't work.
My understanding, is I need to pass it a unique it (eg the key) so it knows which item in state to update. But this may be wrong, and I would like to do this the proper way. I tried the immutability helper, but had no success.
I will also want to update/add/remove nested items in an immutable array.
I put this in a codeSandbox for ease https://codesandbox.io/s/mz5WnOXn
class App extends React.Component {
...
editTitle = (e,changeKey) => {
this.setState({
movies: update(this.state.movies, {changeKey: {name: {$set: e.target.value}}})
})
console.log('Edit title ran on', key)
};
...
render() {
...
return (
<div>
{movies.map(e => {
return (
<div>
<input key={e.pk} onChange={this.editTitle} value={e.name} changeKey={e.pk} type="text" />
With genres:
{e.genres.map(x => {
return (
<span key={x.pk}>
<input onChange={this.editGenres} value={x.name} changeKey={x.pk} type="text" />
</span>
);
})}
</div>
);
})}
</div>
);
}
}
Couple of things,
1. By looking at your data i don't see any pk property maybe its a typo (are you trying to use the id property instead?).
2. The key prop should be on the root element that you return on each iteration of your map function.
3. You can't just add none native attributes to a native Html element instead you can use the data-* like data-changeKey and access it via e.target.getAttribute('data-changeKey')
Something like this:
import { render } from 'react-dom';
import React from 'react';
import { reject, pluck, prop, pipe, filter, flatten, uniqBy } from 'ramda';
import { smallData } from './components/data.js';
import './index.css';
import Result from './components/Result';
import update from 'react-addons-update';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
movies: smallData.movies,
selectedFilters: {},
searchQuery: '',
};
}
editTitle = (e) => {
const attr = e.target.getAttribute('data-changeKey');
this.setState({
movies: update(this.state.movies, {
// what are you trying to do here?
//changeKey: { name: { $set: e.target.value } },
}),
});
console.log('edit title ran on', attr);
};
onSearch = e => {
this.setState({
searchQuery: e.target.value,
});
};
render() {
const { movies } = this.state;
return (
<div>
{movies.map((e, i) => {
return (
<div key={i}>
<input
onChange={this.editTitle}
value={e.name}
data-changeKey={e.id}
type="text"
/>
With genres:
{e.genres.map((x,y) => {
return (
<span key={y}>
<input
onChange={this.editTitle}
value={x.name}
data-changeKey={x.id}
type="text"
/>
</span>
);
})}
Add new genre:
<input type="text" />
</div>
);
})}
</div>
);
}
}
render(<App />, document.getElementById('root'));
Index is what update needs in order to know which data to change.
Firstly, map sure your map is creating an index:
{movies.map((e,index) => {
return (
<input key={e.pk} onChange={e => this.editTitle(e, index)} value={e.name} >
...
Then pass the index to the immutability-helper update like so:
to:
editTitle = (e, index) => {
this.setState({
movies: update(this.state.movies, {[index]: {name: {$set: e.target.value}}})
})