I am learning React and I am trying to use a component to show details of a selected item in a table, my problem is that when I click Next in the table (it is paginated), the state got updated and re-render the component and if I do click many times, it enqueue and the details component is changing between all the items I did click on.
I don't know if there is a good practice for doing this kind of things, i tried to search for something like this but nothing yet.
The component I am using (using public API):
import React from 'react';
class Pokemon extends React.Component {
constructor() {
super();
this.state = {
name: "",
abilities: [],
stats: [],
weight: 0,
height: 0,
base_experience: 0,
};
}
fetch_pokemon = () => {
fetch(this.props.pokemon_url)
.then(res => res.json())
.then(res => {
this.setState({
name: res.name,
abilities: res.abilities,
stats: res.stats,
weight: res.weight,
height: res.height,
base_experience: res.base_experience,
});
});
}
render() {
if(this.props.pokemon_url !== ''){
this.fetch_pokemon();
return (
<div>
<h1>{this.state.name}</h1>
Weight: {this.state.weight}<br />
Height: {this.state.height}<br />
Abilities:
<ul>
{this.state.abilities.map(({ability}, index) => {
return (<li key={index}>{ability.name}</li>);
})}
</ul>
</div>
);
}
else{
return (<h3>Choose one pokémon from the list...</h3>);
}
}
}
export default Pokemon;
My main component:
import React, { Component } from 'react';
//import logo from './logo.svg';
import './App.css';
import Pokemon from './Pokemon';
class App extends Component {
constructor() {
const i_pokemons = 25;
super();
this.state = {
pokemons: [],
pokemons_list: [],
pokemon_show_list: [],
p_init: 0,
p_final: i_pokemons,
pagination: i_pokemons,
pages: 0,
status: '',
enable_buttons: false,
current_page: 0,
current_pokemon_url: '',
URL: `https://pokeapi.co/api/v2/pokemon/?limit=99999`
};
}
componentDidMount(){
this.fetch_pokemons();
}
prev(){
this.setState({
current_page: this.state.current_page - 1,
p_init: this.state.p_init - this.state.pagination,
p_final: this.state.p_final - this.state.pagination
}, () => {
this.fetch_new_page();
});
}
next(){
this.setState({
current_page: this.state.current_page + 1,
p_init: this.state.p_init + this.state.pagination,
p_final: this.state.p_final + this.state.pagination
}, () => {
this.fetch_new_page();
});
}
fetch_new_page = () => {
const current_id = (this.state.current_page - 1) * this.state.pagination;
this.setState({
pokemon_show_list: this.state.pokemons_list.slice(current_id, current_id + 25)
});
this.fetch_pokemons();
}
fetch_pokemons = callback => {
this.setState({
status: 'Waiting for the server and retrieving data, please wait...',
enable_buttons: false
});
return new Promise((resolve, reject) => {
fetch(this.state.URL)
.then(res => res.json())
.then(res => {
if(!res.detail){
this.setState({
pokemons: res,
pokemons_list: res.results,
enable_buttons: true,
status: 'Done',
pokemon_show_list: res.results.slice(this.state.p_init, this.state.p_final)
});
if(this.state.pages === 0){
this.setState({
pages: Math.round(this.state.pokemons_list.length / this.state.pagination),
current_page: 1
});
}
resolve(true);
}else{
reject("Error");
this.setState({status: `Error`});
}
})
.catch(error => {
this.setState({status: `Error: ${error}`});
reject(error);
});
});
}
showPokemon({url}){
this.setState({
current_pokemon_url: url
});
}
render() {
console.log("Render");
return(
<div className="general">
<div className="pokemons-info">
{this.state.status !== '' && this.state.status}
<br />
<table className="pokemon-list">
<thead>
<tr>
<th>Name</th>
<th>More info.</th>
</tr>
</thead>
<tbody>
{this.state.pokemon_show_list.map((pokemon, index) => {
return (
<tr className="l" key={index}>
<td>{pokemon.name}</td>
<td><a className="btn btn-secondary" onClick={this.showPokemon.bind(this, pokemon)} href={`#${pokemon.name}`}>More info.</a></td>
</tr>
);
})}
</tbody>
</table>
<button className="btn btn-primary" disabled={this.state.current_page <= 1} onClick={this.prev.bind(this)}>Prev</button>
Page: {this.state.current_page} of {this.state.pages}
<button className="btn btn-primary" disabled={this.state.current_page === this.state.pages} onClick={this.next.bind(this)}>Next</button>
</div>
<Pokemon pokemon_url={this.state.current_pokemon_url}/>
</div>
);
}
}
export default App;
Feel free for giving any advice
I refactored and clean your code a little bit, but I guess the code below aims what you are looking for. (Read more about functional component 'logicless components').
const API = 'https://pokeapi.co/api/v2/pokemon/';
const PAGE_SIZE = 25;
function Status(props) {
return (
<div>{props.value}</div>
)
}
function Pagination(props) {
return (
<div>
<button
onClick={props.onPrevious}
disabled={props.disabled}>
Prev
</button>
<button
onClick={props.onNext}
disabled={props.disabled}>
Next
</button>
</div>
)
}
function Pokemon(props) {
return (
<div>
<h1>{props.pokemon.name}</h1>
Weight: {props.pokemon.weight}<br />
Height: {props.pokemon.height}<br />
Abilities:
<ul>
{props.pokemon.abilities.map(({ability}, index) => {
return (<li key={index}>{ability.name}</li>);
})}
</ul>
</div>
)
}
function PokemonTable (props) {
return (
<table className="pokemon-list">
<thead>
<tr>
<th>Name</th>
<th>More info.</th>
</tr>
</thead>
<tbody>
{props.children}
</tbody>
</table>
);
}
function PokemonRow (props) {
return (
<tr>
<td>{props.pokemon.name}</td>
<td>
<a href="#" onClick={() => props.onInfo(props.pokemon)}>
More info.
</a>
</td>
</tr>
);
}
class App extends React.Component {
state = {
pokemons: [],
detailedPokemons : {},
loading: false,
status : null,
previous : null,
next : null
}
componentDidMount () {
this.getPokemons(`${API}?limit=${PAGE_SIZE}`)
}
request(url) {
return fetch(url)
.then(blob => blob.json());
}
getPokemons (url) {
this.setState(state => ({
...state,
loading : true,
status : 'Fetching pokemons...'
}));
this.request(url)
.then(response => {
console.log(response)
this.setState(state => ({
...state,
previous : response.previous,
next : response.next,
pokemons : response.results,
loading : false,
status : null
}))
})
.catch(err => {
this.setState(state => ({
...state,
loading : false,
status : 'Unable to retrieved pockemons'
}));
});
}
getPokemonDetail (pokemon) {
const { detailedPokemons } = this.state;
const cachePokemon = detailedPokemons[pokemon.name];
if (cachePokemon !== undefined) { return; }
this.setState(state => ({
...state,
loading : true,
status : `Fetching ${pokemon.name} info`
}));
this.request(pokemon.url)
.then(response => {
this.setState(state => ({
...state,
loading: false,
status : null,
detailedPokemons : {
...state.detailedPokemons,
[response.name]: {
name: response.name,
abilities: response.abilities,
stats: response.stats,
weight: response.weight,
height: response.height,
base_experience: response.base_experience
}
}
}))
})
.catch(err => {
console.log(err)
this.setState(state => ({
...state,
loading : false,
status : 'Unable to retrieved pockemons'
}));
});
}
renderPokemons () {
const { pokemons } = this.state;
return pokemons.map(pokemon => (
<PokemonRow
pokemon={pokemon}
onInfo={this.handleView}
/>
));
}
renderDetailPokemons () {
const { detailedPokemons } = this.state;
return (
<ul>
{Object.keys(detailedPokemons).map(pokemonName => (
<li key={pokemonName}>
<Pokemon pokemon={detailedPokemons[pokemonName]}/>
</li>
))}
</ul>
)
}
handleView = (pokemon) => {
this.getPokemonDetail(pokemon);
}
handlePrevious = () => {
const { previous } = this.state;
this.getPokemons(previous);
}
handleNext = () => {
const { next } = this.state;
this.getPokemons(next);
}
render () {
const { loading, detailedPokemons, status, next, previous } = this.state;
return (
<div className='general'>
<div className="pokemons-info">
{ status && <Status value={status} /> }
<PokemonTable>
{this.renderPokemons()}
</PokemonTable>
<Pagination
disabled={loading}
onPrevious={this.handlePrevious}
onNext={this.handleNext}
/>
{
Object.keys(detailedPokemons).length > 0 &&
this.renderDetailPokemons()
}
</div>
</div>
)
}
}
ReactDOM.render(
<App />,
document.querySelector('#app')
);
Related
What I want is to fetch all users and then based on the username I fetch movies they have watched. I'm still struggling to understand when a state gets changed, more often than not at the end movies is not in the right order, so that when the MovieInfo-Component gets the data the users get the wrong movies assigned.
My code:
export default class Admin extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
movies: [],
};
}
componentDidMount() {
fetch('https://a-url/users/')
.then((res) => res.json())
.then((data) => {
this.setState(
{
users: data.users,
},
() => {}
);
data.users.map((user) => this.fetchMovies(user.name));
});
}
fetchMovies = (user) => {
fetch('https://a-url/' + user + '/movies/')
.then((res) => res.json())
.then((data) => {
this.setState(
{
movies: [...this.state.movies, ...[data.movies]],
},
() => {}
);
});
};
render() {
const { users, movies } = this.state;
return (
<div className='wum__admin section__padding'>
{users.length > 0 &&
movies.length > 0 &&
users.length === movies.length ? (
<>
{movies &&
users &&
users.map((user, i) => (
<MovieInfo
key={i}
movies={movies[user.id - 1]}
id={user.id}
user={user.name}
/>
))}
</>
) : (
<></>
)}
</div>
);
}
}
If you need to filter movies by user you could change the movies state to be an object where each property (user.name) will have its list of movies:
export default class Admin extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
movies: {},
};
}
...
fetchMovies = (user) => {
fetch('https://a-url/' + user + '/movies/')
.then((res) => res.json())
.then((data) => {
const newMovies = { ...this.state.movies };
newMovies[user] = movies;
this.setState(
{
movies: newMovies,
},
() => {}
);
});
};
render() {
const { users, movies } = this.state;
return (
<div className='wum__admin section__padding'>
{users.length > 0 &&
users.map((user, i) => (
<MovieInfo
key={i}
movies={movies[user.name]}
id={user.id}
user={user.name}
/>
))}
</div>
);
}
}
I have been seeing solutions in other questions, such as placing anonymous function to the onClick, removing things from the render, but I keep getting the same problem, the file that marks me where the problem is is the ProductCard.tsx, which has this:
interface PastRouteinfo {
route: string
paymentMethod?: string
}
interface IProps {
settings?: Settings
product: Product
onProductClick?: (product: Product) => void
onGoToCart?: () => void
tracking?: any
cartModel: CartModel
hidePrice?:boolean
history:any
pastRouteInfo?: PastRouteinfo
}
interface IState {
isOpen: boolean
showToast: boolean
nameToRender: any
effectivePrice: any
offerPrice: any
}
class ProductCard extends React.Component<IProps, IState> {
state: IState = {
isOpen: false,
showToast: false,
nameToRender: '',
effectivePrice: 0,
offerPrice: 0
}
componentDidMount() {
const { product } = this.props
const { name } = product
const nameToRender = this.isLongName(name) ? name.slice(0,25) + '...' : name
const effectivePrice = product?.showPrice.price
const offerPrice = product && product.showPrice.offerPrice !== 0 &&
product.showPrice.offerPrice
this.setState({
nameToRender,
effectivePrice,
offerPrice
})
}
componentWillUnmount() {
// fix Warning: Can't perform a React state update on an unmounted component
this.setState = (state,callback)=>{
return;
};
}
onProductClick = () => {
const { onProductClick, product, history, pastRouteInfo = null } = this.props
if(pastRouteInfo !== null) {
this.setState({ isOpen: false })
const data = {
fromSuggested: true,
product,
pastRouteInfo,
}
const routeInfo = `/vendor/${product.providerId}/product/${product._id}`
history.push(routeInfo, data)
return
}
if (!onProductClick && pastRouteInfo === null) {
this.setState({
isOpen: true,
})
} else if(onProductClick) {
onProductClick(product)
}
}
dismissModal = () => {
setTimeout(() => {
this.setState({
isOpen: false,
})
}, 10)
}
setShowToast(showToast: boolean) {
this.setState({ showToast })
}
private renderModal() {
const { isOpen } = this.state
const { product, history } = this.props
const data = {
isOpen,
product
}
const routeInfo = `/vendor/${product.providerId}/product/${product._id}`
history.push(routeInfo, data)
}
validateIfExistLadders = (ladder:any) => ladder && ladder.length > 0
isLongName = (name: string) => name.length >= 25
render() {
const { product, hidePrice } = this.props
const { isOpen, nameToRender, effectivePrice, offerPrice } = this.state
const { filename, brand, ladder } = product
return (
<Fragment>
{isOpen && this.renderModal()}
<div className="product-card" onClick={() => this.onProductClick()}>
<div className="header" ></div>
<div className="body">
<div className="picture" style={{ backgroundImage: `url(${filename})` }}>
{ this.validateIfExistLadders(ladder) &&
<img className="img-ladder" src={small_ladder} alt="Escalonado"/>
}
</div>
<div className="tp">{brand}</div>
<div className="tp" >{nameToRender}</div>
</div>
{offerPrice && !hidePrice ? (
<div className="footer offer">
<Fragment>
<IonText className="tp-old">
<s>{asClp(effectivePrice)}</s>
</IonText>
<IonText className="tp-offer">{asClp(offerPrice)}</IonText>
</Fragment>
</div>
) : (
!hidePrice &&
<div className="footer">
<IonText>{asClp(effectivePrice)}</IonText>
</div>
)}
</div>
</Fragment>
)
}
}
export default track({page: 'ProductCard'})(ProductCard)
I have the following components in react:
PublicProfile.js:
import React, { Component } from 'react';
import axios from 'axios'
import Post from '../posts/Post'
import Navbar from '../Navbar'
import FollowButton from './FollowButton'
import { Avatar, Button, CircularProgress } from '#material-ui/core'
class PublicProfile extends Component {
constructor(props) {
super(props);
this.state = {
user: {},
followers: undefined,
following: undefined,
posts: [],
showFollowers: false,
showFollows: false,
curr_id: null
}
this.handleFollowerClick = this.handleFollowerClick.bind(this)
this.handleFollowClick = this.handleFollowClick.bind(this)
}
componentDidMount() {
const { user_id } = this.props.match.params
axios.get(`http://127.0.0.1:8000/users/${user_id}`)
.then(res =>
this.setState({
user: res.data,
followers: res.data.followers.length,
following: res.data.following.length
}))
.catch(err => console.log(err))
axios.get(`http://127.0.0.1:8000/posts/user/${user_id}`)
.then(res => {
this.setState({ posts: res.data })
})
.catch(err => console.log(err))
axios.get('http://127.0.0.1:8000/users/self')
.then(res => this.setState({curr_id: res.data.id}))
.catch(err => console.log(err))
}
handleFollowerClick(e) {
e.preventDefault()
if (this.state.showFollowers === true) {
this.setState({showFollowers: false})
} else {
this.setState({showFollowers: true})
}
}
handleFollowClick(e) {
e.preventDefault()
if (this.state.showFollows === true) {
this.setState({showFollows: false})
} else {
this.setState({showFollows: true})
}
}
render() {
const showFollowers = this.state.showFollowers
const showFollows = this.state.showFollows
let followers
let follows
let edit
let fbutton
if (showFollowers === true) {
followers = (
<div>
<p>Followed by:</p>
<ul>
{this.state.user.followers.map(follower => (
<li key={follower.id}><a href={`/users/${follower.user.id}`}>{follower.user.username}</a></li>
))}
</ul>
</div>
)
}
if (showFollows === true) {
follows = (
<div>
<p>Follows:</p>
<ul>
{this.state.user.following.map(follow => (
<li key={follow.id}><a href={`/users/${follow.user.id}`}>{follow.user.username}</a></li>
))}
</ul>
</div>
)
}
if (this.state.user.id === this.state.curr_id) {
edit = <Button href='/profile'>Edit My Profile</Button>
}
if (this.state.user.id !== this.state.curr_id) {
fbutton = <FollowButton user={this.state.user} followers_num={this.state.followers} setParentState={state => this.setState(state)} />
}
if (this.state.user.id !== undefined) {
return (
<div style={{background: '#f7f4e9'}}>
<Navbar />
<div style={{height: '70px'}}></div>
<div>
<Avatar style={{width: 75, height: 75}} variant='rounded' src={this.state.user.pp} alt={this.state.user.username} />
<h1>{this.state.user.username}</h1>
<h3>#{this.state.user.username}</h3>
<h4>{this.state.posts.length} Post(s)</h4>
<p>{this.state.user.bio}</p>
<Button style={{marginLeft: '10px'}} disabled={!this.state.following} onClick={this.handleFollowClick}>{this.state.following} Follows</Button>
<Button disabled={!this.state.followers} onClick={this.handleFollowerClick}>{this.state.followers} Followers</Button>
{followers}
{follows}
</div>
{edit}
{fbutton}
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column'
}}>
{this.state.posts.map(post => (
<Post key={post.id} post={post} />
))}
</div>
<div style={{height: '15px'}}></div>
</div>
)
} else {
return (
<div>
<Navbar />
<CircularProgress />
</div>
)
}
}
}
export default PublicProfile
FollowButton.js:
import React, { Component } from 'react';
import axios from 'axios'
import { Button } from '#material-ui/core'
class FollowButton extends Component {
constructor(props) {
super(props);
this.state = {
followsUser: null
}
this.unfollowClick = this.unfollowClick.bind(this)
this.followClick = this.followClick.bind(this)
}
componentDidMount() {
if (this.props.user.id !== undefined) {
axios.get(`http://127.0.0.1:8000/users/check/${this.props.user.id}`)
.then(res => {
this.setState({ followsUser: res.data.follows })
})
.catch(err => console.log(err))
}
}
unfollowClick() {
axios.delete(`http://127.0.0.1:8000/users/${this.props.user.id}/unfollow/`)
.then(() => {
this.setState({ followsUser: false })
this.props.setParentState({followers: this.props.followers_num - 1})
})
.catch(err => console.log(err))
}
followClick() {
axios.post(`http://127.0.0.1:8000/users/${this.props.user.id}/follow/`)
.then(res => {
this.setState({ followsUser: true })
this.props.setParentState({followers: this.props.followers_num + 1})
})
.catch(err => console.log(err))
}
// user: {
// followers: [...this.props.user.followers, res.data.user]
// }
render() {
let button
if (this.state.followsUser) {
button = <Button style={{background: 'blue'}} onClick={this.unfollowClick}>Following</Button>
} else {
button = <Button onClick={this.followClick}>Follow</Button>
}
return (
<div style={{marginTop: '20px'}}>
{button}
</div>
);
}
}
But I get the following error:
index.js:1 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in FollowButton (at PublicProfile.js:93)
I have found that this error is largely due to unresolved process when unmounting the component, but I am not even rendering the component in this case due to the conditional, but I still seem to get the error. Can someone please help me fix it.
You are not cancelling axios request when component unmounted. Axios accepts cancelToken as a parameter, you should create a CancelTokenSource which provides a method cancel and then cancel the Source when component unmounts which cancels all pending request.
Here's the syntax of how to use async/await:
const unfollowClick = async() => {
try{
const res = await axios.delete(`http://127.0.0.1:8000/users/${this.props.user.id}/unfollow/`);
this.setState({ followsUser: false });
this.props.setParentState({followers: this.props.followers_num - 1});
}
catch(err) { console.log(err)}
}
i am trying to convert my class component to a functionnal component but my filters are not working anymore.
the main error is when I want to convert listProducts because I don't know how to define the prevState with useState for that case
how can update the state for case?
this is my code
class component
class ListShopping extends Component{
constructor(props) {
super(props);
this.state = {
size: "",
sort: "",
cartItems: [],
products: [],
filteredProducts: []
};
}
componentDidMount() {
fetch("http://localhost:8000/products")
.then(res => res.json())
.catch(err =>
fetch("db.json")
.then(res => res.json())
.then(data => data.products)
)
.then(data => {
this.setState({ products: data });
this.listProducts();
});
}
listProducts = () => {
this.setState(state => {
if (state.sort !== "") {
state.products.sort((a, b) =>
state.sort === "lowestprice"
? a.price < b.price
? 1
: -1
: a.price > b.price
? 1
: -1
);
} else {
state.products.sort((a, b) => (a.id > b.id ? 1 : -1));
}
if(state.size !== ""){
return {
filteredProducts: state.products.filter(
a => a.availableSizes.indexOf(state.size.toUpperCase()) >= 0
)
}
}
return { filteredProducts: state.products };
});
};
handleSortChange = e => {
this.setState({ sort: e.target.value });
this.listProducts();
};
handleSizeChange = e => {
this.setState({ size: e.target.value });
this.listProducts();
};
render() {
return (
<div className="container">
<h1>E-commerce Shopping Cart Application</h1>
<hr />
<div className="row">
<div className="col-md-9">
<Filter
count={this.state.filteredProducts.length}
handleSortChange={this.handleSortChange}
handleSizeChange={this.handleSizeChange}
/>
<hr />
<Products
products={this.state.filteredProducts}
/>
</div>
</div>
</div>
);
}
}
functionnal component
const ListShopping = () => {
const [data, setData] = useState({
products : [],
filteredProducts : [],
sort : '',
size : ''
})
const {products, filteredProducts, sort, size} = data;
const fetchApi = () => {
axios.get(`http://localhost:8000/products`)
.then(response => response.data)
.then(data => {
setData({...data, products: data});
})
}
const listProducts = () => {
};
const handleSortChange = e => {
setData({...e, sort: e.target.value})
listProducts();
};
const handleSizeChange = e => {
setData({...e, size: e.target.value})
listProducts();
};
useEffect(()=> {
fetchApi()
}, [])
return(
<div className="container">
<h1>E-commerce Shopping Cart Application</h1>
<hr />
<div className="row">
<div className="col-md-9">
<Filter
count={filteredProducts.length}
handleSortChange={handleSortChange}
handleSizeChange={handleSizeChange}
/>
<hr />
<Products
products={filteredProducts}
/>
</div>
</div>
</div>
)
}
Try this
const listProducts = () => {
setData(data => {
if (data.sort !== '') {
data.products.sort((a, b) =>
data.sort === 'lowestprice'
? a.price < b.price
? 1
: -1
: a.price > b.price
? 1
: -1,
);
} else {
data.products.sort((a, b) => (a.id > b.id ? 1 : -1));
}
if (data.size !== '') {
return {
...data,
filteredProducts: data.products.filter(
a => a.availableSizes.indexOf(data.size.toUpperCase()) >= 0,
),
};
}
return { ...data, filteredProducts: data.products };
});
};
Suppose you have a state like this
const [todos, setTodos] = useState([1,2,3,4,5,6,7]);
Method 1:
Now if you want to get prevState from this hook try this way
setTodos(oldTodo => {
oldTodo.push(1000);
setTodos(oldTodo)
})
this oldTodo work the same way the prevState works.
Method 2:
const listProducts = () => {
let oldState = todos
//Now do what you want to do with it. modify the oldTodo
setTodos(oldTodo);
}
I prefer the second method because it gives me more flexibility to change modify the state and if you cannot manage the state properly in the first method or if it finds any bug then it will return undefined so I prefer to work taking the whole state in a temporary variable and after work is done set it
I have a JSON file:
[
{
"id": 1,
"text": "Hello",
"availability": false
},
{
"id": 2,
"text": "Hello",
"availability": true
}
]
What I would like to achieve is for the text to automatically change from hello to goodbye when availability : false. If availability : true then I would like it to stay the same displaying 'Hello'.
This is my code so far:
import React, { Component } from 'react';
import './styles.css'
class GetOnlinePosts extends Component {
constructor(props){
super(props);
this.state = {
error : null,
isLoaded : false,
posts : []
};
}
componentDidMount(){
fetch("https://api.myjson.com")
.then( response => response.json())
.then(
(result) => {
this.setState({
isLoaded : true,
posts : result
});
},
(error) => {
this.setState({
isLoaded: true,
error
})
},
)
}
render() {
const {error, isLoaded, posts} = this.state;
const orderedPosts = [...posts.filter((post) => post.availability), ...posts.filter((post) => !post.availability)]
if(error){
return <div>Error in loading</div>
}else if (!isLoaded) {
return <div>Loading ...</div>
}else{
return(
<div>
<div className="tiles">
{
orderedPosts.map(post => (
<div key={post.id}>
<div className="tile">
<p className="greeting">{post.text}</p>
</div>
</div>
))
}
</div>
</div>
);
}
}
}
export default GetOnlinePosts;
Any help on changing the text from 'Hello' to 'Goodbye' when availability : false and keeping the text 'Hello' when availability : true would be great. thanks in advance
Please add condition to map
<p className="greeting">{post.availability ? post.text : 'Goodbye'}</p>
Please changes this line
import React, { Component } from 'react';
import './styles.css'
class GetOnlinePosts extends Component {
constructor(props){
super(props);
this.state = {
error : null,
isLoaded : false,
posts : []
};
}
componentDidMount(){
fetch("https://api.myjson.com")
.then( response => response.json())
.then(
(result) => {
this.setState({
isLoaded : true,
posts : result
});
},
(error) => {
this.setState({
isLoaded: true,
error
})
},
)
}
render() {
const {error, isLoaded, posts} = this.state;
const orderedPosts = [...posts.filter((post) => post.availability), ...posts.filter((post) => !post.availability)]
if(error){
return <div>Error in loading</div>
}else if (!isLoaded) {
return <div>Loading ...</div>
}else{
return(
<div>
<div className="tiles">
{
orderedPosts.map(post => (
<div key={post.id}>
<div className="tile">
<p className="greeting">{post.availability ? post.text : 'Goodbye'}</p> // Change this line
</div>
</div>
))
}
</div>
</div>
);
}
}
}
export default GetOnlinePosts;
Somewhere after fetching, map the results like this:
const processPost = post => post.availability
? post
: Object.assign({}, post, { text: "Goodbye" });
// ...
fetch("https://api.myjson.com")
.then(response => response.json())
.then(posts => posts.map(processPost))
This is easy to achieve with simple ternary statement.
return(
<div>
<div className="tiles">
{
orderedPosts.map(post => (
<div key={post.id}>
<div className="tile">
<p className="greeting">{post.availability ? post.text : 'Goodbye!'}</p>
</div>
</div>
))
}
</div>
</div>
);
Docs for ternary: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator
You can use ternary operator for displaying text.I hope it will helps you.
import React, { Component } from 'react';
import './styles.css'
class GetOnlinePosts extends Component {
constructor(props){
super(props);
this.state = {
error : null,
isLoaded : false,
posts : []
};
}
componentDidMount(){
fetch("https://api.myjson.com")
.then( response => response.json())
.then(
(result) => {
this.setState({
isLoaded : true,
posts : result
});
},
(error) => {
this.setState({
isLoaded: true,
error
})
},
)
}
render() {
const {error, isLoaded, posts} = this.state;
const orderedPosts = [...posts.filter((post) => post.availability), ...posts.filter((post) => !post.availability)]
if(error){
return <div>Error in loading</div>
}else if (!isLoaded) {
return <div>Loading ...</div>
}else{
return(
<div>
<div className="tiles">
{
orderedPosts.map(post => (
<div key={post.id}>
<div className="tile">
{
post.availability:( <p className="greeting">{post.text}</p>)?
(<p className="greeting">Goodbye</p>)
}
</div>
</div>
))
}
</div>
</div>
);
}
}
}
export default GetOnlinePosts;