Can I change class component into functional component? - Reactjs - javascript

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;

Related

Display the data from 'this.state.data'?

Goal:
*Get the data of of variable Cars to the 'this.state.data' when you have retrieved the data from API.
*Display data from 'this.state.data' and not using the variable Cars.
Problem:
I do not know how to do it and is is it possible to do it when you have applied refactoring SOLID?
Info:
I'm newbie in React JS.
Stackblitz:
https://stackblitz.com/edit/react-v39jre?
App.js
import React from 'react';
import './style.css';
import CarsList from './components/CarsList';
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state = {
name: 'React',
data: null
};
}
render() {
return (
<div className="App">
<CarsList />
</div>
);
}
}
export default App;
CarsList.jsx
import React, { useState, useEffect } from 'react';
this.state = {
name: 'React',
data: null
};
const CarsList = () => {
const [cars, setCars] = useState([]);
useEffect(() => {
const fetchCars = async () => {
const response = await fetch(
'https://jsonplaceholder.typicode.com/users'
);
setCars(await response.json());
};
fetchCars();
}, []);
return (
<div>
{cars.map((car, index) => (
<li key={index}>
[{++index}]{car.id} - {car.name}$
</li>
))}
</div>
);
};
export default CarsList;
After getting the response in the child component you should do a callback function which can be passed as prop from parent to child. Using the function you can pass the data from child to parent and update the parent state.
App.js
import { useState } from "react";
import CarsList from "./CarsList";
import "./styles.css";
export default function App() {
const [state, setState] = useState([]);
const handleUpdateParentState = (data) => {
setState(data);
};
console.log("state in parent", state);
return (
<div>
<CarsList updateParentState={handleUpdateParentState} />
</div>
);
}
CarsList.js
import React, { useState, useEffect } from "react";
const CarsList = (props) => {
const [cars, setCars] = useState([]);
useEffect(() => {
const fetchCars = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
const data = await response.json();
setCars(data);
props?.updateParentState(data);
} catch (error) {
console.log(error);
}
};
fetchCars();
}, []);
return (
<ul>
{cars?.map((car, index) => (
<li key={index}>
[{++index}]{car.id} - {car.name}$
</li>
))}
</ul>
);
};
export default CarsList;
Codesandbox
Data can be shared using props but from parent component to child component only. We cannot pass child component state to parent component through props.
Though we can create a function at parent level and pass it to child component as props so we can execute there.
In your case, you have to create a function in App component and pass it on carList component as props. In carList component you do not have to create the cars state. After fetching the cars from API just call the function you passed from App component
App.js
import React from 'react';
import './style.css';
import CarsList from './components/CarsList';
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state = {
name: 'React',
data: null
};
}
function setCarList(cars) {
this.setState({
date: cars
});
}
render() {
return (
<div className="App">
<CarsList setCars={setCarList}/>
</div>
);
}
}
export default App;
CarList.js
import React, {useEffect } from 'react';
this.state = {
name: 'React',
data: null
};
const CarsList = (props) => {
useEffect(() => {
const fetchCars = async () => {
const response = await fetch(
'https://jsonplaceholder.typicode.com/users'
);
this.props.setCars(await response.json());
};
fetchCars();
}, []);
return (
<div>
{cars.map((car, index) => (
<li key={index}>
[{++index}]{car.id} - {car.name}$
</li>
))}
</div>
);
};
export default CarsList;
It doesn't make much sense for each CarList component to load data if you're going to have loads of them and they're going to share information with each other. You should load all your data in your App component using an array of API fetch calls and then use Promise.all to extract and parse the data, and then add it to the state. That state can be then shared with all your Carlist components.
Here's a React component:
const {Component} = React;
const json = '["BMW", "Clio", "Merc", "Fiat"]';
// Simulates an API call
function mockFetch() {
return new Promise((res, rej) => {
setTimeout(() => res(json), 1000);
});
}
class App extends Component {
constructor() {
super();
this.state = { cars: [] };
}
componentDidMount() {
// Have an array fetches (you would supply each one a
// different API endpoint in your code)
const arr = [mockFetch(), mockFetch(), mockFetch()];
// Grab the json, `map` over it and parse it
Promise.all(arr).then(data => {
const cars = data.map(arr => JSON.parse(arr));
// Then set the new state
this.setState(prev => ({ ...prev, cars }));
});
}
// You can now send the data to your small functional
// carlist components
render() {
const { cars } = this.state;
if (!cars.length) return <div />;
return (
<div>
<Carlist cars={cars[0]} />
<Carlist cars={cars[1]} />
<Carlist cars={cars[2]} />
</div>
)
}
};
function Carlist({ cars }) {
return (
<ul>{cars.map(car => <div>{car}</div>)}</ul>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
And here's equivalent written as a functional component with hooks:
const {useState, useEffect} = React;
const json = '["BMW", "Clio", "Merc", "Fiat"]';
function mockFetch() {
return new Promise((res, rej) => {
setTimeout(() => res(json), 1000);
});
}
function App() {
const [cars, setCars] = useState([]);
// This works in the same way as the previous example
// except we're not setting `this.state` we're setting the
// state called `cars` that we set up with `useState`.
useEffect(() => {
function getData() {
const arr = [mockFetch(), mockFetch(), mockFetch()];
Promise.all(arr).then(data => {
const cars = data.map(arr => JSON.parse(arr));
setCars(cars);
});
}
getData();
}, []);
if (!cars.length) return <div />;
return (
<div>
<Carlist cars={cars[0]} />
<Carlist cars={cars[1]} />
<Carlist cars={cars[2]} />
</div>
);
};
function Carlist({ cars }) {
return (
<ul>{cars.map(car => <div>{car}</div>)}</ul>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

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;

Props are showing empty value above `render` function while it work inside `render` function?

So I am passing props from app.js to state.jsx but while console logging the props it give me a undefined but it perfectly works inside the render function. Why that's happening?
App.js
class App extends React.Component{
state = {
data : {},
states: '',
}
async componentDidMount () {
const dataFromApi = await StateData();
this.setState({ data: dataFromApi})
}
handleStateChange = async(states) => {
const fetchedData = await StateData(states);
this.setState({data: fetchedData, states: states})
}
render(){
const {data, states} = this.state;
console.log(data);
return (
<div className="App">
<StateCard data={data} states={states}/>
</div>
);
}
}
State.jsx
export default class StateCard extends React.Component{
constructor(props) {
//this doesn't work
super(props)
const dta = this.props.data.confirmed
console.log(dta)
}
// spacing deafult value is 8px , so the 3*8=24px width column
render(){
//This works
console.log(this.props.data.confirmed)
console.log(this.props.states)
const {confirmed,active, deaths, recovered} = this.props.data
return (
<div>
<span>Confirmed : {confirmed}</span>
</div>);}
}
you can use useEffect and Function component instead of React.Component.
it's more easier then using componentdidMount and other lifecycle methods.
import React, { useEffect } from "react";
const StateCard = props => {
const { data, states } = props;
const { confirmed, active, deaths, recovered } = data;
useEffect(() => {
console.log(confirmed);
console.log(states);
}, [props]);
return (
<div>
<span>Confirmed : {confirmed}</span>
</div>
);
};
export default StateCard;

Foreach loop in return statement of react

I have fetched some information an API and now I am trying to show the information fetched from it. The information which I have fetched includes books_authors , books_id's , price and the dataset is quite large and I am unable to display this information from my following approach...can someone help me with it... I am new to react
This is what I have tried so far:
import React from "react";
import Head from './head';
function App(){
let s;
const proxy = 'http://cors-anywhere.herokuapp.com/';
const api = `${proxy}http://starlord.hackerearth.com/books`;
fetch(api)
.then(response =>{
return response.json();
})
.then(data =>{
console.log(data);
data.forEach((index) => {
s=index;
<Head s/>
});
});
return(
<Head />
);
}
export default App;
//the head component
import React from "react";
function Head(props){
return(
<div className="app">
<div className="heading">
<h1>BOOK_CAVE</h1>
<div className="heading_description">So many books...so
little time...</div>
</div>
<div className="author">{props.authors}</div>
<div className="id">{props.bookID}</div>
<div className="price">{props.price}</div>
</div>
);
}
export default Head;
You can do this using Hooks, useState to store data and useEffect to call API,
import React, {useState,useEffect} from "react";
import Head from './head';
function App(){
const [data, setData] = useState([])
useEffect(() => {
const proxy = 'http://cors-anywhere.herokuapp.com/';
const api = `${proxy}http://starlord.hackerearth.com/books`;
fetch(api).then(response => {
setData(response.json())
})
},[])
return(
<div>
{data.length>0 && data.map(book => <Head book={book} />)
</div>
);
}
And you Head component should be,
function Head(props){
return(
<div className="app">
<div className="heading">
<h1>BOOK_CAVE</h1>
<div className="heading_description">So many books...so
little time...</div>
</div>
<div className="author">{props.book.authors}</div>
<div className="id">{props.book.bookID}</div>
<div className="price">{props.book.price}</div>
</div>
);
}
The books array you fetch from the API should be stored in a state and you should render the app according to that state. The data fetching should happen when the component mounted, so you make the call on componentDidMount lifecycle method, and update the state when the data finished fetching. Also, the Head component recieves three props, but you pass only one.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
books: [],
fetching: true,
}
componentDidMount() {
const proxy = 'http://cors-anywhere.herokuapp.com/';
const api = `${proxy}http://starlord.hackerearth.com/books`;
fetch(api)
.then(response => response.json() )
.then(data => this.setState({books: data, fetching: false,}) );
}
render() {
if (this.state.fetching) {
return <div>Loading...</div>
}
const headArray = this.state.books.map(book => (
<Head
authors={book.authors}
bookID={book.bookID}
price={book.price}
/>
));
return(
<div>
{headArray}
</div>
);
}
}
You need to:
Enclose the fetch n a lifecycle method or a useEffect hook
Put the API's response in a state (which will cause a re-render)
Iterate over the state in the return statement, using map, not forEach
Example using hooks:
function App(){
const [apiData, setApiData] = useState([])
const [isLoading, setIsLoading] = useState(true)
useEffect(
() => {
const proxy = 'http://cors-anywhere.herokuapp.com/';
const api = `${proxy}http://starlord.hackerearth.com/books`;
fetch(api).then(response => {
setApiData(response.json())
setIsLoading(false)
})
},
[]
)
const authors = data.map((index) => index.authors).flat()
return(
<div>
{authors.map((author) => <Head author{author} />)
</div>
);
}

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;

Categories

Resources