Content not displayed despite present data - React - reactjs

Could you tell me why my offer content is always empty ?
The "toto" is not displayed and my data are displayed because of the line "console.log(offers").
const ListProduct = (offers : any) => {
console.log(offers);
const offersDisplay = offers ? (
<div>
{ () => {
console.log("test");
offers.map((shop :any) => {
shop.offers.map((offer:any) => {
return(
<div className="card border-secondary mb-3 listMaxWidth">
<div className="card-header">{shop.name}</div>
<div className="card-body">
<img src={offer.picture} className="imgOffer"/>
<h4 className="card-title">{offer.name}</h4>
<p className="card-text">{shop.description}</p>
</div>
</div>
);
});
})
}
}
</div>
):'toto';
return(
<div>
{offersDisplay }
</div>
)
}
export default ListProduct;
I tried so many different way to write it, unfortunately can't find the right way ...
Could you help me please ?
Thanks in advance

You don't need to pass a callback function in your code
const offersDisplay = offers ? (
<div>
//this callback
{ () => {
And also you don't return from your first map.
And the last thing is that you need to include the code in your return that way it gets executed every time the offers data is changed.
You need to change your code like this
const ListProduct = (offers : any) => {
return(
<div>
{
offers && offers.length && offers.map((shop :any) => {
return shop.offers.map((offer:any) => {
return(
<div className="card border-secondary mb-3 listMaxWidth">
<div className="card-header">{shop.name}</div>
<div className="card-body">
<img src={offer.picture} className="imgOffer"/>
<h4 className="card-title">{offer.name}</h4>
<p className="card-text">{shop.description}</p>
</div>
</div>
);
});
})
}
</div>
)
}
export default ListProduct;

Related

Use State not updating as expected

Fairly new to react and trying to build a clone of The Movie Database site. I want this toggle switch to change my api call from movies to tv. It starts working after a couple clicks, but then it throws everything off and it's not displaying the correct items anyway. Not really sure what's going on here...or even why it starts working after two clicks. Anyone know whats up with this?
import React, { useState, useEffect } from "react";
import axios from "axios";
import API_KEY from "../../config";
const Popular = ({ imageUri }) => {
// GET POPULAR MOVIES
const [popularMovies, setPopularMovies] = useState("");
const [genre, setGenre] = useState("movie");
console.log(genre);
const getPopular = async () => {
const response = await axios.get(
`https://api.themoviedb.org/3/discover/${genre}?sort_by=popularity.desc&api_key=${API_KEY}`
);
setPopularMovies(response.data.results);
};
useEffect(() => {
getPopular();
}, []);
const listOptions = document.querySelectorAll(".switch--option");
const background = document.querySelector(".background");
const changeOption = (el) => {
let getGenre = el.target.dataset.genre;
setGenre(getGenre);
getPopular();
listOptions.forEach((option) => {
option.classList.remove("selected");
});
el = el.target.parentElement.parentElement;
let getStartingLeft = Math.floor(
listOptions[0].getBoundingClientRect().left
);
let getLeft = Math.floor(el.getBoundingClientRect().left);
let getWidth = Math.floor(el.getBoundingClientRect().width);
let leftPos = getLeft - getStartingLeft;
background.setAttribute(
"style",
`left: ${leftPos}px; width: ${getWidth}px`
);
el.classList.add("selected");
};
return (
<section className="container movie-list">
<div className="flex">
<div className="movie-list__header">
<h3>What's Popular</h3>
</div>
<div className="switch flex">
<div className="switch--option selected">
<h3>
<a
data-genre="movie"
onClick={(e) => changeOption(e)}
className="switch--anchor"
>
In Theaters
</a>
</h3>
<div className="background"></div>
</div>
<div className="switch--option">
<h3>
<a
data-genre="tv"
onClick={(e) => changeOption(e)}
className="switch--anchor"
>
On TV
</a>
</h3>
</div>
</div>
</div>
<div className="scroller">
<div className="flex flex--justify-center">
<div className="flex flex--nowrap container u-overScroll">
{popularMovies &&
popularMovies.map((movie, idX) => (
<div key={idX} className="card">
<div className="image">
<img src={imageUri + "w500" + movie.poster_path} />
</div>
<p>{movie.title}</p>
</div>
))}
</div>
</div>
</div>
</section>
);
};
export default Popular;
You're using the array index as your key prop when you're mapping your array.
You should use an id that is specific to the data that you're rendering.
React uses the key prop to know which items have changed since the last render.
In your case you should use the movie id in your key prop instead of the array index.
popularMovies.map((movie) => (
<div key={movie.id} className="card">
<div className="image">
<img src={imageUri + 'w500' + movie.poster_path} />
</div>
<p>{movie.title}</p>
</div>
));
Also
You're calling the api directly after setGenre. However state changes aren't immediate. So when you're making your api call you're still sending the last movie genre.
Two ways of fixing this:
You could call your function with the genre directly, and change your function so it handles this case:
getPopular('movie');
Or you could not call the function at all and add genre as a dependency of your useEffect. That way the useEffect will run each time the genre change.
useEffect(() => {
getPopular();
}, [genre]);
PS: You should consider splitting your code into more component and not interacting with the DOM directly.
To give you an idea of what it could look like, I refactored a bit, but more improvements could be made:
const Popular = ({ imageUri }) => {
const [popularMovies, setPopularMovies] = useState('');
const [genre, setGenre] = useState('movie');
const getPopular = async (movieGenre) => {
const response = await axios.get(
`https://api.themoviedb.org/3/discover/${movieGenre}?sort_by=popularity.desc&api_key=${API_KEY}`
);
setPopularMovies(response.data.results);
};
useEffect(() => {
getPopular();
}, [genre]);
const changeHandler = (el) => {
let getGenre = el.target.dataset.genre;
setGenre(getGenre);
};
const isMovieSelected = genre === 'movie';
const isTvSelected = genre === 'tv';
return (
<section className="container movie-list">
<div className="flex">
<MovieHeader>What's Popular</MovieHeader>
<div className="switch flex">
<Toggle onChange={changeHandler} selected={isMovieSelected}>
In Theaters
</Toggle>
<Toggle onChange={changeHandler} selected={isTvSelected}>
On TV
</Toggle>
</div>
</div>
<div className="scroller">
<div className="flex flex--justify-center">
<div className="flex flex--nowrap container u-overScroll">
{popularMovies.map((movie) => {
const { title, id, poster_path } = movie;
return (
<MovieItem
title={title}
imageUri={imageUri}
key={id}
poster_path={poster_path}
/>
);
})}
</div>
</div>
</div>
</section>
);
};
export default Popular;
const Toggle = (props) => {
const { children, onChange, selected } = props;
const className = selected ? 'switch--option selected' : 'switch--option';
return (
<div className={className}>
<h3>
<a
data-genre="movie"
onClick={onChange}
className="switch--anchor"
>
{children}
</a>
</h3>
<div className="background"></div>
</div>
);
};
const MovieHeader = (props) => {
const { children } = props;
return (
<div className="movie-list__header">
<h3>{children}</h3>
</div>
);
};
const MovieItem = (props) => {
const { title, imageUri, poster_path } = props;
return (
<div key={idX} className="card">
<div className="image">
<img src={imageUri + 'w500' + poster_path} />
</div>
<p>{title}</p>
</div>
);
};

React Owl Carousel 2.3.3 - I loose dynamic data after refreshing page. Working fine on local data

I am using json-server and fetching data using fetch. First time I get data properly in an Owl Carousel and the slider works fine but after when I refresh the page all my dynamic data wipe out. Still my carousel slides but with no data. Also I have attached a jquery script https://code.jquery.com/jquery-3.2.1.slim.min.js in index.html.
I am loosing my Owl Carousel Dynamic Data which I am using through map method in the Owl Carousel Component. Below is the code where I am using my slider. Please help me to find where I am doing wrong. Thank You.
---------------
<<<<<- Below code is MainSlider.js ->>>>>
import React, { useReducer, useEffect } from "react";
import OwlCarousel from "react-owl-carousel";
import "owl.carousel/dist/assets/owl.carousel.css";
import { mainSliderReducer } from "../reducers/mainSliderReducer";
const API = "http://localhost:8000/mainSlider";
const initialState = {};
const MainSlider = () => {
const [state, dispatch] = useReducer(mainSliderReducer, initialState);
const { data } = state;
useEffect(() => {
getData();
}, []);
const getData = () => {
fetch(API)
.then((res) => {
if (res.ok) {
return res.json();
} else {
console.log("DATA NOT FOUND. SOME ERROR");
throw new Error("ERROR FETCHING DATA");
}
})
.then((data) => dispatch({ type: "GET_MAINSLIDER_DATA", payload: data }))
.catch((err) => console.log(err));
};
console.log(data);
return (
<>
<OwlCarousel className="owl-theme" loop margin={10} nav>
{data ? (
data.map((item) => {
const { id, heading, description, img, smallHeading } = item;
return (
<section key={id} className="dvMainSlider">
<div className="item bg bgcolor1 pb-md-5 pt-md-4 py-xl-0 h-100vh h-sm-auto h-xl-100vh">
<div className="container">
<div className="row slideInfo h-xl-100vh align-items-xl-center">
<div className="col-md-6 text-center">
<img
src={img}
className="img-fluid d-inline-block"
alt=""
/>
</div>
<div className="col-md-6 pt-lg-5 pt-xl-0 description">
<h1 className="text-white">{heading}</h1>
<h4 className="text-white">{smallHeading}</h4>
<p className="text-white">{description}</p>
<a href="--" className="btn btnPrimary mb-3 mt-sm-3">
Shop More
</a>
</div>
</div>
</div>
</div>
</section>
);
})
) : (
<h1>"SLIDE NOT FOUND"</h1>
)}
</OwlCarousel>
</>
);
};
export default MainSlider;
<<<<<- Below code is mainSliderReducer.js ->>>>>
export const mainSliderReducer = (state, action) => {
console.log(state, action);
switch (action.type) {
case "GET_MAINSLIDER_DATA":
return { ...state, data: action.payload };
default:
return state;
}
};
Hey I found my own question's answer and i found the way which i am sharing to you all. Just check if data is available then only you load the Owl Carousel Component. Check the code below for better understanding.
{data && (
<OwlCarousel {...options}>
{data.map((item) => {
const { id, heading, description, img, smallHeading } = item;
return (
<section key={id} className="dvMainSlider">
<div className="item bg bgcolor1 pb-md-5 pt-md-4 py-xl-0 h-100vh h-sm-auto h-xl-100vh">
<div className="container">
<div className="row slideInfo h-xl-100vh align-items-xl-center">
<div className="col-md-6 text-center">
<img
src={img}
className="img-fluid d-inline-block"
alt=""
/>
</div>
<div className="col-md-6 pt-lg-5 pt-xl-0 description">
<h1 className="text-white">{heading}</h1>
<h4 className="text-white">{smallHeading}</h4>
<p className="text-white">{description}</p>
<a href="--" className="btn btnPrimary mb-3 mt-sm-3">
Shop More
</a>
</div>
</div>
</div>
</div>
</section>
);
})}
</OwlCarousel>
)}

Why is my state's value is always undefined?

I've read all the question asked that similar to my problem , but it still cant's solve my issues . I'm fetching datas from an api and assign it's values into my state , the program compiled successfully but this message appears in the browser "TypeError: weatherData.main is undefined"
Here's my code
import './App.css';
import React,{useEffect,useState} from 'react';
function App() {
const [weatherData,setWeatherData] = useState({});
const [position,setPosistion] = useState({});
useEffect(()=>{
navigator.geolocation.getCurrentPosition( pos => {
setPosistion(()=>{
return {
latitude : pos.coords.latitude,
longitude : pos.coords.longitude
}
})
});
},[])
useEffect(()=>{
fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${position.latitude}&lon=${position.longitude}&units=metric&appid=a*****5`)
.then( res => res.json() )
.then( resJson => {
setWeatherData(()=>resJson)
})
},[position]);
return (
<div className="App">
<Weather weatherData={weatherData} />
</div>
);
}
const Weather = ({weatherData}) => {
return(
<React.Fragment>
<div className="location-time">
<span id="location">{weatherData.name}</span>
</div>
<div className="weather">
<span className="temp">{`${weatherData.main.temp} C`}</span>
<div className="icon"></div>
<div className="description">{weatherData.weather[0].main}</div>
<div className="low-max">{`max : ${weatherData.main.temp_max} min : ${weatherData.main.temp_min}`}</div>
<div className="feels-like">{`feels like : ${weatherData.main.feels_like}`}</div>
<button >REFRESH</button>
</div>
</React.Fragment>
)
}
export default App;
the state contains
{"coord":{"lon":106.8451,"lat":-6.2146},"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"base":"stations","main":{"temp":302.76,"feels_like":307.06,"temp_min":300.15,"temp_max":305.93,"pressure":1007,"humidity":74},"visibility":6000,"wind":{"speed":2.57,"deg":340},"clouds":{"all":40},"dt":1613038370,"sys":{"type":1,"id":9383,"country":"ID","sunrise":1612997835,"sunset":1613042198},"timezone":25200,"id":1642911,"name":"Jakarta","cod":200}
Before the data is fetched, weather is initialised to be a empty object and hence weather.main is undefined.
You should consider rendering a fallback page till weather data is fetched
const Weather = ({weatherData}) => {
if(Object.keys(weatherData).length === 0) {
return <div>{/* Somee info here or maybe a loader*/}</div>
}
return(
<React.Fragment>
<div className="location-time">
<span id="location">{weatherData.name}</span>
</div>
<div className="weather">
<span className="temp">{`${weatherData.main.temp} C`}</span>
<div className="icon"></div>
<div className="description">{weatherData.weather[0].main}</div>
<div className="low-max">{`max : ${weatherData.main.temp_max} min : ${weatherData.main.temp_min}`}</div>
<div className="feels-like">{`feels like : ${weatherData.main.feels_like}`}</div>
<button >REFRESH</button>
</div>
</React.Fragment>
)
}

Toggle specific div (id) within a react component

I have a site built with post-components to show articles in a feed. Inside the component, I have a button that opens a modal onClick. I use useState to toggle on the modal which works perfectly fine. The problem is that since the toggle is put on the modal-div inside the component.. every single post modal opens whenever I click one of the buttons. I want to open only the targeted post modal (with the sam post id as the button I’m clicking). I can’t figure out how to do this…in react.
const [toggle, setToggle] = useState (true);
const toggler = () => {
setToggle(prev => !prev)
}
...
return (
<section className="posts">
{data.allMarkdownRemark.edges.map((edge) => {
return (
<div className="post">
<div className="postDescrip">
<h2 className="postTitle">{edge.node.frontmatter.title}</h2>
<h2 className="name">{edge.node.frontmatter.name}</h2>
<button className="readMoreBtn" onClick={toggler}>{toggle ? <h2 className="readMore">Read more</h2> : <h2 className="readMore">Read less</h2>}
</button>
</div>
<Img className="postImg" fluid={edge.node.frontmatter.featuredImage.childImageSharp.fluid} />
<div className={toggle ? 'hide' : 'postCopy'} >
<Close close={toggler} />
<h3>{edge.node.frontmatter.details}</h3>
<div className="copy" dangerouslySetInnerHTML= {{__html: edge.node.html}}></div>
<h4>Read the full article in Issue One</h4>
</div>
</div>
)}
)}
</section>
)
}
export default Posts;
After trying suggested solution using object instead on bolean. I now receive this error message
[Error message][1]for the following code:
const [toggles, setToggles] = useState ({});
let id;
const createToggler = (id) = () => {
setToggles(prev => { [id] : !prev[id] })
// setToggle(prev => { ...prev, [id]: !prev[id] }) // or support multi modal at same time. but I think you don't want it.
}
const data = useStaticQuery(graphql`
query {
allMarkdownRemark (
sort: { order: DESC, fields: [frontmatter___date] }
){
edges {
node {
frontmatter {
id
title
name
details
featuredImage {
childImageSharp {
fluid(maxWidth: 800) {
...GatsbyImageSharpFluid
}
}
}
}
html
fields {
slug
}
}
}
}
}
`)
return (
<section className="posts">
{data.allMarkdownRemark.edges.map((edge) => {
const id = edge.node.frontmatter.id;
const toggle = toggles[id];
const toggler = createToggler(id);
return (
<div className="post" id={edge.node.frontmatter.id}>
<div className="postDescrip">
<h2 className="postTitle">{edge.node.frontmatter.title}</h2>
<h2 className="name">{edge.node.frontmatter.name}</h2>
<button className="readMoreBtn" onClick={toggler}>{toggle ? <h2 className="readMore">Read more</h2> : <h2 className="readMore">Read less</h2>}
</button>
</div>
<Img className="postImg" fluid={edge.node.frontmatter.featuredImage.childImageSharp.fluid} />
<div className={toggle ? 'hide' : 'postCopy'} id={edge.node.frontmatter.id}>
<Close close={toggler} />
<h3>{edge.node.frontmatter.details}</h3>
<div className="copy" dangerouslySetInnerHTML= {{__html: edge.node.html}}></div>
<h4>Read the full article in Issue One</h4>
</div>
</div>
)}
)}
</section>
)
}
export default Posts;
[1]: https://i.stack.imgur.com/VhIYF.png
like this.
use a object instead of a single boolean.
const [toggles, setToggles] = useState ({});
const createToggler = (id) = () => {
setToggle(prev => { [id]: !prev[id] }) // atmost one id is true. others is undefine or false.
// setToggle(prev => { ...prev, [id]: !prev[id] }) // or support multi modal at same time. but I think you don't want it.
}
...
return (
<section className="posts">
{data.allMarkdownRemark.edges.map((edge) => {
const id = ... // get your id form edge.
const toggle = toggles[id];
const toggler = createToggler(id);
return (
<div className="post">
<div className="postDescrip">
<h2 className="postTitle">{edge.node.frontmatter.title}</h2>
<h2 className="name">{edge.node.frontmatter.name}</h2>
<button className="readMoreBtn" onClick={toggler}>{toggle ? <h2 className="readMore">Read more</h2> : <h2 className="readMore">Read less</h2>}
</button>
</div>
<Img className="postImg" fluid={edge.node.frontmatter.featuredImage.childImageSharp.fluid} />
<div className={toggle ? 'hide' : 'postCopy'} >
<Close close={toggler} />
<h3>{edge.node.frontmatter.details}</h3>
<div className="copy" dangerouslySetInnerHTML= {{__html: edge.node.html}}></div>
<h4>Read the full article in Issue One</h4>
</div>
</div>
)}
)}
</section>
)
}
export default Posts;
I solved my problem like this
import React, {useState} from "react"
import Img from "gatsby-image"
import './posts.css';
import cancel from '../images/cancel.png'
const Post = ({title, name, id, image, details, html}) => {
const [toggle, setToggle] = useState (true);
const toggler = () => {
setToggle(prev => !prev)
}
const selectPost= (event) =>{
let id = event.target.id,
postCopy = document.getElementById('hide' + id);
toggler.call(postCopy);
}
return (
<div className="post">
<div className="postDescrip">
<h2 className="postTitle">{title}</h2>
<h2 className="name">{name}</h2>
<button className="readMoreBtn" onClick={selectPost}>{toggle ? <h2 id={id} className="readMore">Read more</h2> : <h2 id={id} className="readMore">Read less</h2>}
</button>
</div>
<Img className="postImg" fluid={image} />
<div id={'hide' + id} className={toggle ? 'hide' : 'postCopy'} >
<button aria-label="Close" onClick={selectPost} className="closeBtn">
<img alt="close-button" src={cancel}/>
</button>
<h3>{details}</h3>
<div className="copy" dangerouslySetInnerHTML= {html}></div>
<h4>Read the full article in Issue One</h4>
</div>
</div>
)
}
export default Post;

Force update to make functional component re-render

I'm doing pokedex (pokemon wiki stuff). I want to change my component view, when clicking on pokemon images (description lookalike). When I click on an image - nothing happens (firstly, I want at least pokemon's name to be added to the pokemonDescription array). What am I doing wrong?
let pokemonDescription = [];
const useForceUpdate = () => {
const [value, setValue] = useState(true);
return () => setValue(value => !value);
}
const forceUpdate = useForceUpdate();
const onPokemonClick = (event) => {
console.log(
"wrapper clicked, event.target - ",
event.target.getAttribute('data-name')
);
pokemonDescription = [];
pokemonDescription.push(event.target.getAttribute('data-name'));
console.log("description array -", pokemonDescription);
forceUpdate();
};
useEffect(() => {
document.querySelector(".wrapper").addEventListener("click", onPokemonClick);
...
return () => {
document.querySelector(".wrapper").removeEventListener("click", onPokemonClick);
};
}, []);
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
data-name={p.name}
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{ pokemonDescription }
</div>
</div>
)
You should add pokemonDescription to your component state
const [pokemonDescription, setPokemonDescription] = useState([]);
Remove the forceUpdate function and hook, it is unnecessary.
Attach the click handlers to the elements with the data-name attribute you are trying to handle.
Map the pokemonDescription state array to renderable JSX. I simply used a div, but you should use whatever your UI design requires.
const onPokemonClick = (event) => {
setPokemonDescription(names => [
...names,
event.target.getAttribute('data-name'),
]);
};
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
data-name={p.name}
onClick={onPokemonClick} // <-- attach click handler to img element
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{pokemonDescription.map(name => (
<div>{name}</div>
))}
</div>
</div>
)
Add pokemonDescription to state instead of some local variable and it will solve your issue.
Try to avoid using forceUpdate, most of the times it means only that you are doing something silly.
I don't what that useForceUpdate does , but here is how would go about adding pokemon names to description array which is a state variable in my answer
const [pokemonDescription , setPokemonDescription ] = useState(null);
const onPokemonClick = (p) => {
const tempPokemonDescription = [...pokemonDescription ];
pokemonDescription.push(p.name);
console.log("description array -", pokemonDescription);
setPokemonDescription(tempPokemonDescription )
};
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" onClick={e=>onPokemonClick(p)} key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{ pokemonDescription }
</div>
</div>
)

Resources