Fetching data from an api and manipulating state - javascript

I'm trying to make a quote generator, and I'm having a weird problem while passing the data provided from the api to my state, can someone help me with that? (I don't know why, but when I click the button to generate a quote the browser throws the error: '_this2.setState is not a function') Here are the two components used on the App:
import React from "react";
import "./styles.css";
import QuoteComponent from "./QuoteComponent";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
qouteData: ""
};
}
fetchData() {
fetch("https://api.quotable.io/quotes")
.then(res => {
return res.json();
})
.then(data => {
this.setState({
quoteData: data
});
});
}
componentDidMount() {
this.fetchData();
}
render() {
const { qouteData } = this.state;
return (
<div className="App">
<QuoteComponent
quote={this.state.qouteData.content}
author={qouteData.author}
handleClick={this.fetchData}
/>
</div>
);
}
}
export default App;
Quote Component:
import React from "react";
import "./styles.css";
function QuoteComponent(props) {
return (
<div className="card">
<div className="container">
<div className="flex">
<div id="quote">
<h2>{props.quote}</h2>
</div>
<div id="quote-a">
<h4>{props.author}</h4>
</div>
<div id="button">
<button onClick={props.handleClick}>New Quote</button>
</div>
</div>
</div>
</div>
);
}
export default QuoteComponent;

fetchData() {
fetch("https://api.quotable.io/quotes")
.then(res => {
return res.json();
})
.then(data => {
this.setState({
quoteData: data
});
});
}
in this code this is bound to its context which is fetchData. And fetchData has no member setState.
In order to avoid this being bound to the function fetchData you can use an arrow function:
fetchData = () => {
fetch("https://api.quotable.io/quotes")
.then(res => {
return res.json();
})
.then(data => {
this.setState({
quoteData: data
});
});
}
Here is more information about binding this in react

Related

Can I change class component into functional component? - Reactjs

I use class component. Just testing it. Now I don't know how to convert it into functional. This is my code:
class PostList extends Component {
constructor(props) {
super(props);
this.state = {
info: []
};
}
componentDidMount() {
axios
.get("https://api2.binance.com/api/v3/ticker/24hr")
.then((response) => {
this.setState({
info: response.data
});
console.log(response.data);
});
}
render() {
const { info } = this.state;
return (
<div>
<h2>post!</h2>
{info.map((user) => (
<div key={user.symbol}>
<h6>{user.priceChange}</h6>
</div>
))}
</div>
);
}
}
export default PostList;
I should use this code in my redux and I need to convert it into functional component
I want something like this:
export const PostList = () =>{
return (
//my code using axios,
)
}```
For functional component, you should use hooks instead of classify components default state and componentDidMount. So for define a new state you need to use useState and for componentDidMount you need to use useEffect:
const PostList = (props) => {
const [state,setState] = useState({info:[]});
useEffect(()=>{
axios
.get("https://api2.binance.com/api/v3/ticker/24hr")
.then((response) => {
setState({
info: response.data
});
console.log(response.data);
});
},[])
const { info } = state;
return (
<div>
<h2>post!</h2>
{info.map((user) => (
<div key={user.symbol}>
<h6>{user.priceChange}</h6>
</div>
))}
</div>
);
}
export default PostList;
Since you want to use functional components, you will have to use React Hooks!
In your case, using the useState and useEffect hooks can help to achieve the same result as this.state and componentDidMount() respectively.
const PostList = (props) => {
const [state,setState] = useState({info:[]});
useEffect(()=>{
axios
.get("https://api2.binance.com/api/v3/ticker/24hr")
.then((response) => {
setState({
info: response.data
});
console.log(response.data);
});
},[])
return (
<div>
<h2>post!</h2>
{state.info.map((user) => (
<div key={user.symbol}>
<h6>{user.priceChange}</h6>
</div>
))}
</div>
);
}
export default PostList;

How to do a for loop with React and why is my Component not getting rendered

import React, { Component } from "react";
import Pokecard from "./Pokecard";
import "./Pokedex.css";
class Pokedex extends Component {
static defaultProps = {
pokemon: [],
getData() {
for (let i = 1; i <= 40; i++) {
fetch(`https://pokeapi.co/api/v2/pokemon/${i}`)
.then((response) => response.json())
.then((data) => {
this.pokemon.push({
id: data.id,
namePoke: data.name,
type: data.types[0].type.name,
base_experience: data.base_experience,
});
});
}
},
};
render() {
this.props.getData();
return (
<div className="Pokedex">
<div className="Pokedex-grid">
{this.props.pokemon.map((p) => (
<Pokecard
id={p.id}
name={p.namePoke}
type={p.type}
exp={p.base_experience}
key={p.id}
/>
))}
</div>
</div>
);
}
}
export default Pokedex;
I am new to React and I don't understand why my for loop runs multiple times so I get the Pokecards twice or more.
Also, the whole Component Pokedex is not showing up when reloading the page. What do I do wrong?
#azium made a great comment. You are calling to get data in your render, which is setting state, and causing a re-render, which is calling getData again, which is fetching data again and then setting state again, and the cycle continues on and on indefinitely. Also, default props should only define properties default values, but in this case you don't need a getData default prop. All you need to do is call the getData method in your componentDidMount. And your method needs to store the data in state, and not do a direct property change (like you are doing). Here is an example:
import React, { Component } from "react";
import Pokecard from "./Pokecard";
import "./Pokedex.css";
class Pokedex extends Component {
static state = {
pokemon: []
};
componentDidMount() {
this.getData();
}
getData() {
for (let i = 1; i <= 40; i++) {
const pokemon = [...this.state.pokemon];
fetch(`https://pokeapi.co/api/v2/pokemon/${i}`)
.then((response) => response.json())
.then((data) => {
pokemon.push({
id: data.id,
namePoke: data.name,
type: data.types[0].type.name,
base_experience: data.base_experience,
});
});
this.setState({pokemon});
}
}
render() {
return (
<div className="Pokedex">
<div className="Pokedex-grid">
{this.state.pokemon.map((p) => (
<Pokecard
id={p.id}
name={p.namePoke}
type={p.type}
exp={p.base_experience}
key={p.id}
/>
))}
</div>
</div>
);
}
}
export default Pokedex;

Searchfield based on the data in ReactJS

I'm a newbie in React. I'm creating a little app that has
users and I need to create a searchbar that will look for users if I
type two or three lettters of users name. But obviously I stuck. So
any help will be nice. Thanks in advance
import React, { Component } from 'react'
class FilterForm extends Component {
state = {
query: '',
user: [],
searchString:[]
}
handleInputChange = (e) => {
this.setState ({
query:e.target.value
} ,()=>{
this.filterArray();
})
}
getData = () => {
fetch(`http.//localhost:3000/login`)
.then(response => response.json())
.then(responseData => {
//console.log(responseData)
this.setState ({
user:responseData,
searchString: responseData
})
})
}
filterArray = () => {
let searchString = this.state.query;
let responseData = this.state.user;
if(searchString.length > 0){
//console.log(responseData[i].first_name)
searchString = responseData.filter(searchString);
this.setState({ responseData })
}
}
componentWillMount() {
this.getData();
}
render() {
return(
<div className="searchform">
<form>
<input
type="text"
id="filter"
placeholder="Search for user..."
onChange={this.handleInputChange}/>
</form>
<div>{this.state.searchString.map(i => <p>{i.first_name}</p>)}
</div>
</div>
)
}
}
export default FilterForm
And this is my App.js
import React from 'react';
import PeopleList from "./components/PeopleList"
import FilterForm from "./components/FilterForm"
//import { search } from "./Utils"
//import UserData from "./components/UserData";
//import SearchBox from "./components/SearchBox"
import './App.css';
class App extends React.Component {
render() {
return (
<React.Fragment>
<div>
<FilterForm />
<PeopleList />
</div>
</React.Fragment>
);
}
}
export default App
And when I start typing something in searchbar I get an error:
I edit your post and did some indentation, and to your question, your problem is in fillterArray, this is not how you use filter method and you are setting state to user witch is not relevant to the search.
try this:
filterArray = () => {
const { query, user } = this.state;
if(query.length > 0){
const searchResult = searchString.filter((el, i) => el[i].first_name.includes(query);
this.setState({ searchString: searchResult })
}
}
more info about filter method:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

How do I manipulate data that is located in state and display to page?

I'm making three separate axios calls that each set the state with some data. Where do I do my data manipulation with the state data not to change the state but to display something else where?
For example out of the transactionItems state, I want to get all transactions for the current date. All transaction items have the date set automatically when its added to the database.
I'm having issues parsing the data because my setstate seems to update 3 times with all the axios calls.
There are other data manipulations I would like to be able to do as well but I feel like I'll hit another roadblock.
import React, { Component } from "react";
import axios from "axios";
import moment from "moment";
import TransactionSummary from "./TransactionSummary";
import BudgetSummary from "./BudgetSummary";
import DebtSummary from "./DebtSummary";
class DashboardTable extends Component {
constructor(props) {
super(props);
this.state = {
transactionItems: [],
budgetItems: [],
debtItems: [],
spentToday: ""
};
}
componentDidMount() {
this.getTransactionData();
this.getBudgetData();
this.getDebtData();
}
getTransactionData = () => {
axios.defaults.headers.common["Authorization"] = localStorage.getItem(
"jwtToken"
);
axios
.get("/api/transactions")
.then(res =>
this.setState({
transactionItems: res.data
})
)
.catch(err => console.log(err));
};
getBudgetData = () => {
axios.defaults.headers.common["Authorization"] = localStorage.getItem(
"jwtToken"
);
axios
.get("/api/budgets")
.then(res =>
this.setState({
budgetItems: res.data
})
)
.catch(err => console.log(err));
};
getDebtData = () => {
axios.defaults.headers.common["Authorization"] = localStorage.getItem(
"jwtToken"
);
axios
.get("/api/debts")
.then(res =>
this.setState({
debtItems: res.data
})
)
.catch(err => console.log(err));
};
render() {
return (
<div>
<div className="content">
<TransactionSummary transactionItems={this.state.transactionItems} />
<BudgetSummary budgetItems={this.state.budgetItems} />
<DebtSummary debtItems={this.state.debtItems} />
</div>
</div>
);
}
}
export default DashboardTable;
Here's DebtSummary component
import React from "react";
const DebtSummary = props => {
let sumOfDebtItems = props.debtItems.reduce((a, c) => {
return a + c["balance"];
}, 0);
return (
<div>
<p>Debt Summary</p>
{sumOfDebtItems}
</div>
);
};
export default DebtSummary;
Like Hemadri said, the easiest way to do this is to move the 3 axios calls into their respective component
You can also move the data manipulation into a separate method and call it in the render method. You can write as many of these as you need, they can all read from the same state variable
DebtSummary example:
import React from "react";
class DebtSummary extends React.Component {
constructor(props) {
super(props);
this.state = {
debtItems: []
}
}
getDebtData = () => {
axios.defaults.headers.common["Authorization"] = localStorage.getItem(
"jwtToken"
);
axios
.get("/api/debts")
.then(res =>
this.setState({
debtItems: res.data
})
)
.catch(err => console.log(err));
};
// Do some data manipulation, in the case computing the debt sum
sumOfDebtItems = () => {
return this.state.debtItems.reduce((a, c) => {
return a + c["balance"];
}, 0);
}
// Load the debt data once the component has mounted
componentDidMount() {
this.getDebtData()
}
render() {
return (
<div>
<p>Debt Summary</p>
{this.sumOfDebtItems()}
</div>
);
}
};
export default DebtSummary;

React.js - Loading single post data from API correctly

I am fairly new to React, and trying to work my way through how I should properly be loading data from my API for a single post.
I have read that I should be using "componentDidMount" to make my GET request to the API, but the request is not finished by the time the component renders. So my code below does not work, as I am recieving the error: "Cannot read property setState of undefined".
What I am doing wrong here? Should I be calling setState from somewhere else? My simple component is below - thanks.
import React from 'react';
import Header from './Header';
import axios from 'axios';
class SingleListing extends React.Component {
constructor(props) {
super(props);
this.state = {
listingData: {}
}
}
componentDidMount() {
// Get ID from URL
var URLsegments = this.props.location.pathname.slice(1).split('/');
// Load the listing data
axios.get('/api/listing/' + URLsegments[1])
.then(function(res){
let listingDataObject = res.data;
console.log(listingDataObject);
this.setState({
listingData: listingDataObject
});
})
.catch(function(err){
console.log(err);
});
}
render() {
console.log('helsdfdsfsdflssosso');
console.log(this.state.listingData);
return (
<div className="SingleListing">
<Header />
<div className="container">
<div>Property Address: {this.state.listingData.propertyAddress}</div>
This is a single listing
</div>
</div>
)
}
}
export default SingleListing;
You just need to change what you render depending on whether the data is loaded or not yet.
Also, you should use arrow functions when handling the axios response, otherwise this is not set correctly.
class SingleListing extends React.Component {
constructor(props) {
super(props);
this.state = {
listingData: null,
};
}
componentDidMount() {
// Get ID from URL
const URLsegments = this.props.location.pathname.slice(1).split('/');
// Load the listing data
axios
.get(`/api/listing/${URLsegments[1]}`)
.then(res => {
const listingDataObject = res.data;
console.log(listingDataObject);
this.setState({
listingData: listingDataObject,
});
})
.catch(err => {
console.log(err);
});
}
render() {
const isDataLoaded = this.state.listingData;
if (!isDataLoaded) {
return <div>Loading...</div>;
}
return (
<div className="SingleListing">
<Header />
<div className="container">
<div>Property Address: {this.state.listingData.propertyAddress}</div>
This is a single listing
</div>
</div>
);
}
}
export default SingleListing;
this is out of scope you need to include it. here is a solution using es2015 arrow functions =>
axios.get('/api/listing/' + URLsegments[1])
.then((res) => {
let listingDataObject = res.data;
console.log(listingDataObject);
this.setState({
listingData: listingDataObject
});
})
.catch((err) => {
console.log(err);
});

Categories

Resources