I am fetching a JSON from a file in one ReactJS component and trying to display it but I don't know why something is not working.
what am I doing wrong?
App.js
import React from 'react';
import Home from './components/Home';
import GameIntro from './components/GameIntro';
import {BrowserRouter,Switch,Route} from 'react-router-dom';
function App() {
return (
<div className='container mt-5 py-5'>
<div className="row">
<div className="col-md-6 offset-md-3">
<BrowserRouter>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/intro' component={GameIntro} />
</Switch>
</BrowserRouter>
</div>
</div>
</div>
)
}
export default App;
JSON file:
gamedata.js
export const gdatas = [
{
"data": [
{
"id": 0,
"image": "https://images.unsplash.com/photo-1492725764893-90b379c2b6e7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "Mom"
},
{
"id": 1,
"image": "https://images.unsplash.com/photo-1482235225574-c37692835cf3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "Dad"
}
]
},
{
"data": [
{
"id": 2,
"image": "https://images.unsplash.com/photo-1551445523-324a0fdab051?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "Apple"
},
{
"id": 3,
"image": "https://images.unsplash.com/photo-1553279768-865429fa0078?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "Mango"
},
{
"id": 4,
"image": "https://images.unsplash.com/photo-1502741338009-cac2772e18bc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "Blueberry"
}
]
},
{
"data": [
{
"id": 5,
"image": "https://images.unsplash.com/photo-1459411621453-7b03977f4bfc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "broccoli"
},
{
"id": 6,
"image": "https://images.unsplash.com/photo-1531170887152-6b21ba4ce8ae?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "cucumber"
},
{
"id": 7,
"image": "https://images.unsplash.com/photo-1564874997803-e4d589d5fd41?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "tomato"
},
{
"id": 8,
"image": "https://images.unsplash.com/photo-1506807803488-8eafc15316c7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "beetroot"
}
]
},
{
"data": [
{
"id": 9,
"image": "https://images.unsplash.com/photo-1518674660708-0e2c0473e68e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "Pen"
},
{
"id": 10,
"image": "https://images.unsplash.com/photo-1516962215378-7fa2e137ae93?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "Pencil"
},
{
"id": 11,
"image": "https://images.unsplash.com/photo-1541963463532-d68292c34b19?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "Book"
},
{
"id": 12,
"image": "https://images.unsplash.com/photo-1527239441953-caffd968d952?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "Papers"
},
{
"id": 13,
"image": "https://images.unsplash.com/photo-1551818014-7c8ace9c1b5c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
"word": "Pendrive"
}
]
}
]
the file in which i am trying to display JSON is this:
GameIntro.js
import React from 'react';
import {gdatas} from './gamedata';
const GameIntro = () => {
const gameData = gdatas.map(gd => {
gd.data.map(elm =>(
<div className="card">
<img src={elm.image} className="card-img-top" alt={elm.word} />
<div className="card-body">
<h5 className="card-title mt-3">{elm.word}</h5>
</div>
</div>
))
})
return (
<div className='container'>
<div className="row">
<div className="col-md-6">
{gameData}
</div>
</div>
</div>
)
}
export default GameIntro;
This component GameIntro.js is unable to display JSON.
You are not returning any data from the map function.
Add the return and add a key and you good to go:
const GameIntro = () => {
const gameData = gdatas.map(gd => {
return gd.data.map(elm =>(
<div key={elm.id} className="card">
<img src={elm.image} className="card-img-top" alt={elm.word} />
<div className="card-body">
<h5 className="card-title mt-3">{elm.word}</h5>
</div>
</div>
))
});
return (
<div className='container'>
{gameData}
</div>
)
}
Related
I have nested json and arrays that are located in there. So the thing I am trying to do is this. I want to make a Search functionality, that 'functionality' would go through the arrays and based on a specific Id it would display the name of that specific object in the array. I have tried with Context api to share the state globally, I know its not the cleanest way of doing it, anyway it is giving me an error in FreeToPlayComponent ".filter is not a function".
Context
import React, { useState, useContext } from 'react';
export const SearchContext =React.createContext(null)
export default function SearchProvider({children}) {
const [searchValue, setSearchValue] = React.useState("");
function filterProduct(product) {
return product.name.toLowerCase().includes(searchValue.toLowerCase());
}
return(
<SearchContext.Provider value ={{filterProduct, searchValue, setSearchValue}}>
{children}
</SearchContext.Provider>
); }
json
[
{
"freetoplay": [{
"id": "0",
"image": "src=fsdf",
"price": "60$",
"name": "CS Go"
},
{
"id": "1",
"image": "src=fsdf",
"price": "6$",
"name": "Fifa"
}
],
"action": [{
"id": "2",
"image": "src=fsdf",
"price": "60$",
"name": "doom"
},
{
"id": "3",
"image": "src=fsdf",
"price": "66$",
"name": "cyberpunk"
}
],
"adventure": [
{
"id": "4",
"image": "src=fsdf",
"price": "60$",
"name": "indiana "
},
{
"id": "5",
"image": "src=fsdf",
"price": "43$",
"name": "torchlight"
}
]
}
]
Search Component
import React from 'react'
import './Search.css'
import { SearchContext } from './SearchContext';
function Search() {
const {searchValue, setSearchValue}=React.useContext(SearchContext);
return (
<div className='search'>
<form className="search__Form">
<input className="search__Input" type="text"
value ={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
type='text'
placeholder='Search '/>
</form>
</div>
)
}
export default Search
import React from 'react'
import Header from './Header'
import './App.css';
import SlideShow from './SlideShow';
import Routes from './Routes';
import data from "./data.json";
import SearchProvider from "./SearchContext";
function App() {
return (
<div className="app">
<SearchProvider>
<Header />
<SlideShow />
<Routes />
</SearchProvider>
</div>
);
}
export default App;
import React from 'react'
import data from "./data.json";
import {
Link
} from "react-router-dom";
import { SearchContext } from './SearchContext';
function FreeToPlay() {
const {filterProduct}=React.useContext(SearchContext);
return (
<>
<div className='All' >
{data[0].filter(filterProduct).freetoplay.map((product) => {
return (
<div className='f2p' key={product.id}>
<img src={product.image}></img>
<h2>{product.name}</h2>
<h5>{product.price}</h5>
<Link
to={`/payment/${product.id}`}
className='link'
>
Buy Now
</Link>
</div>
);
})}
</div>
</>
);
}
export default FreeToPlay
Your JSON data appears to be invalid, it has an extraneous opening curly bracket before the "action" key.
json
[
{
"freetoplay": [{
"id": "0",
"image": "src=fsdf",
"price": "60$",
"name": "CS Go"
},
{
"id": "1",
"image": "src=fsdf",
"price": "6$",
"name": "Fifa"
}
],
{ // <-- remove this!!
"action": [{
"id": "2",
"image": "src=fsdf",
"price": "60$",
"name": "doom"
},
{
"id": "3",
"image": "src=fsdf",
"price": "66$",
"name": "cyberpunk"
}
],
"adventure": [
{
"id": "4",
"image": "src=fsdf",
"price": "60$",
"name": "indiana "
},
{
"id": "5",
"image": "src=fsdf",
"price": "43$",
"name": "torchlight"
}
]
}
]
Also, based on the data shape and what your filterProduct function does
function filterProduct(product) {
return product.name.toLowerCase().includes(searchValue.toLowerCase());
}
it seems you should also be filtering the category/product array versus the outer array of categories/products since the category/product elements have the "name" property.
Change
data[0].filter(filterProduct).freetoplay.map
to
data[0].freetoplay.filter(filterProduct).map
Hello again stackoverflow community. I have F2P component who is is 'maping' objects in json and displaying them. In that Component is located a buy Now link which leads to the payment page and Component. What should happen is this, when I press on the Link it should display image class and name of that Component, but for some reason nothing happens. Why is that and what should I do to fix it?
import React from 'react'
import data from "../data.json";
import {
Link
} from "react-router-dom";
function FreeToPlay({product}) {
return (
<>
<div className='All' >
{data[0].freetoplay.map((product) => {
return (
<div className='f2p' key={product.id}>
<img src={product.image}></img>
<h2>{product.name}</h2>
<h5>{product.price}</h5>
<Link
to={`/payment/${product.id}`}
className='link'
>
Buy Now
</Link>
</div>
);
})}
</div>
</>
);
}
export default FreeToPlay
json
[
{
"freetoplay": [
{
"id": "0",
"image": "src=fsdf",
"price": "60$",
"name": "CS Go"
},
{
"id": "1",
"image": "src=fsdf",
"price": "6$",
"name": "Fifa"
}
],
{
"action": [
{
"id": "2",
"image": "src=fsdf",
"price": "60$",
"name": "doom"
},
{
"id": "3",
"image": "src=fsdf",
"price": "66$",
"name": "cyberpunk"
}
],
"adventure": [
{
"id": "4",
"image": "src=fsdf",
"price": "60$",
"name": "indiana "
},
{
"id": "5",
"image": "src=fsdf",
"price": "43$",
"name": "torchlight"
}
],
}
]
import React from 'react'
import { useParams, Link } from "react-router-dom";
import data from "../data.json";
function Payment() {
const { productId } = useParams();
const filteredData = data.filter((product) => product.id === productId)[0];
return (
<div className='Payment'>
<img src={filteredData.image}></img>
<h1>{filteredData.price}</h1>
<h1>{filteredData.name}</h1>
</div>
)
}
export default Payment
Routes
function Routes() {
return (
<div>
{data.map((product) =>
<Router>
<SideBar />
<Route path="/payment/:productId">
<Payment/>
</Route>
<Switch>
<Route path="/action">
<Action key={product.id} product={product} />
</Route>
<Route path="/freetoplay">
<FreeToPlay key={product.id} product={product} />
</Route>
</Switch>
</Router>
)}
</div>
)
}
export default Routes
I receive JSON from my back end, I save it in my state and I want to use it in props in another react component, but it doesn't work.
I try to show need date like that in props of my component - {this.state.movies["0"]["title"]}, but it doesn't work.
constructor() {
super();
this.state = {
movies: []
}
}
componentDidMount() {
this.getAllMoviesForMainPage();
}
getAllMoviesForMainPage() {
axios.get("http://localhost:8080/showAll")
.then(response => {
this.setState({ movies: response.data })
})
}
render() {
return (
<div className="qwerty">
<MainPageComponent />
<div className='moviePreviewGrid'>
<Router>
<div className="moviePreviewGrid-row">
<div className="moviePreviewGrid-col">
<MoviePreview
Title={this.state.movies["2"]["title"]}
MoviePreviewAvatar={DrivePoster}
SeeMore="unrelated long string here"
/>
<NavLink to="/showByTitle/Драйв">
<button type="button" className="myBtn">See more</button>
</NavLink>
</div>
and structure of my JSON
[
{
"id": 1,
"title": "Джокер",
"releaseDate": "2019",
"genre": "Триллер, драма, криминал",
"duration": "122 минуты",
"rating": 8.7,
"criticReviews": [
{
"criticName": "Anton",
"review": "anton review"
},
{
"criticName": "OldCritic",
"review": "old review"
}
],
"userReviews": [
{
"nickName": "Igor",
"review": "igor review"
},
{
"nickName": "Nik",
"review": "nik review"
}
]
},
{
"id": 2,
"title": "Ирландец",
"releaseDate": "2019",
"genre": "Драма, триллер, криминал, биографический",
"duration": "209 минут",
"rating": 8.4,
"criticReviews": [
{
"criticName": "YoungCritic",
"review": "young review"
}
],
"userReviews": [
{
"nickName": "Gambit",
"review": "gambit review"
},
{
"nickName": "Andrew",
"review": "andrew review"
}
]
},
{
"id": 3,
"title": "Драйв",
"releaseDate": "2011",
"genre": "Боевик, драма",
"duration": "100 минут",
"rating": 7.8,
"criticReviews": [
{
"criticName": "Critic",
"review": "review"
}
],
"userReviews": [
{
"nickName": "Anton",
"review": "anton review"
}
]
},
{
"id": 4,
"title": "Последний человек на Земле",
"releaseDate": "2015",
"genre": "Комедия",
"duration": "22 минуты",
"rating": 7.3,
"criticReviews": [
{
"criticName": "NewCritic",
"review": "new review"
}
],
"userReviews": [
{
"nickName": "Atomf7",
"review": "atomf7 review"
}
]
},
{
"id": 5,
"title": "Интерстеллар",
"releaseDate": "2014",
"genre": "Фантастика, драма, приключения",
"duration": "169 минут",
"rating": 8.6,
"criticReviews": [
{
"criticName": "Nik",
"review": "nik review"
}
],
"userReviews": [
{
"nickName": "Alice",
"review": "alice review"
}
]
}
]
and i wont to have for example title of first movie
In the first init, the this.state.movies has a length of 0 so this.state.movies["2"]["title"] of course would have no value at all
because getAllMoviesForMainPage is async (axios call) and takes a little longer to complete, so first you must give it an initial value and only when the request completes you can give it the real value.
example:
<MoviePreview
Title={this.state.movies.length > 0 ? this.state.movies[2].title : ""}
MoviePreviewAvatar={DrivePoster}
/>
Usually a state, isLoading is used for this case. So you can know when you received your data.
In the first render movies are not already fetched from the api.
So you need to conditionally render it.
import React, { Component } from "react";
import axios from "axios";
export default class Test extends Component {
constructor() {
super();
this.state = {
movies: [],
loading: true
}
}
componentDidMount() {
this.getAllMoviesForMainPage();
}
getAllMoviesForMainPage() {
axios.get("http://localhost:8080/showAll")
.then(response => {
this.setState({ movies: response.data, loading: false })
})
}
render() {
const { loading, movies } = this.state;
if (loading) {
return <div>Loading...</div>;
} else {
return (
<div>
{movies.length > 0 ? (
<div>First movie title: {movies[0].title}</div>
) : (
<div>No movies</div>
)}
</div>
);
}
}
}
You can check this example using a fake api.
You are making a GET request in componentDidMount which is async so until the promise is resolve in .then(.. your state does not contain response.data in movies.
The important thing to remember is component gets re-render every time you do this.setState(.. , so before you do setState in your getAllMoviesForMainPage method, the initial render will happen and it will contain an empty array in this.state.movies that you have defined in constructor
Also since your response contains array of objects, you would be using movies['2'].title instead of movies['2']['title']
So your render method should contain something like this to show movie preview:
<div className="moviePreviewGrid-col">
{this.state.movies.length ? (
<MoviePreview
Title={this.state.movies['2'].title}
MoviePreviewAvatar={DrivePoster}
SeeMore="unrelated long string here"
/>
) : (
<PlaceHolderPreview />
)}
<NavLink to="/showByTitle/Драйв">
<button type="button" className="myBtn">
See more
</button>
</NavLink>
</div>
PlaceHolderPreview can be your another component that will show -- well a placeholder for preview.
Hope it all makes sense.
The JSON backend guys provide me its multiple parent child so I have to put the dynamic loop to show parent child.
JSON
"data": [
{
"id": 25,
"slug": "mobiles",
"parent_id": null,
"name": "Mobiles"
},
{
"id": 26,
"slug": "mobile-phones-accessories",
"parent_id": 25,
"name": "Mobile Phones accessories"
},
{
"id": 27,
"slug": "computer-laptop",
"parent_id": null,
"name": "Computer & Laptop"
},
{
"id": 28,
"slug": "laptops",
"parent_id": 27,
"name": "Laptops"
},
{
"id": 29,
"slug": "mobile-phones",
"parent_id": 26,
"name": "Mobiles Phone"
}
]
My Function (Kindly ignore this. It's just a try but I have got 1 child parent)
renderCategoriesHtml() {
const { categories } = this.props;
if (!categories) return false;
const nullCat = [];
categories.map((obj) => {
if (obj.parent_id == null) {
nullCat.push(obj);
}
});
return nullCat.map(
(parentCat, i) => (
<div className="form-group" key={i}>
<div className="checkbox" key={i}>
<label>
<Field
name={`categories.${parentCat.id}`}
component="input"
type="checkbox"
/>
{parentCat.slug}
</label>
</div>
{
categories.map(
(childCat, j) => (
parentCat.id == childCat.parent_id ?
<div className="checkbox ml-20" key={j}>
<label>
<Field
name={`categories.${childCat.id}`}
component="input"
type="checkbox"
/>
{childCat.slug}
</label>
</div>
: ''
)
)
}
</div>
)
);
}
I want this (That dynamic html i want)
<ul>
<li>mobiles</li>
<ul>
<li>mobile-phones-accessories</li>
<ul>
<li>mobile-phones</li>
</ul>
</ul>
<li>computer-laptop</li>
<ul>
<li>laptops</li>
</ul>
</ul>
Try this:
class TreeRender extends React.Component {
state = {
data: JSON.parse('[{"id": 25,"slug": "mobiles","parent_id": null,"name": "Mobiles"},{"id": 26,"slug": "mobile-phones-accessories","parent_id": 25,"name": "Mobile Phones accessories"},{"id": 27,"slug": "computer-laptop","parent_id": null,"name": "Computer & Laptop"},{"id": 28,"slug": "laptops","parent_id": 27,"name": "Laptops"},{"id": 29,"slug": "mobile-phones","parent_id": 26,"name": "Mobiles Phone"}]')
}
getCurrent = (node) => this.state.data.filter(cNode => cNode.parent_id == node).map(cNode => (
<ul key={`node_${cNode.id}`}>
<li>{cNode.name}</li>
{this.getCurrent(cNode.id)}
</ul>
))
render() {
return (
<div>
{this.getCurrent(null)}
</div>
);
}
}
FIDDLE
I have a simple data structure that lists some information. The problem is when I click the icon it opens the both content. I didn't find a solution how to open the current div.
class HotSpots extends React.Component {
constructor(props, context) {
super(props, context);
this.handleClick = this.handleClick.bind(this);
this.state = {
isOpen: false,
icon: 'fa-plus'
}
}
handleClick(event) {
console.log(this.state.isOpen, 'clicked');
this.setState({
isOpen: !this.state.isOpen,
icon: this.state.icon === 'fa-plus' ? 'fa-remove' : 'fa-plus'
})
}
render() {
//console.log(DATA.module.hotSpots.popups);
let items = DATA.module
.hotSpots
.popups.map(n =>
<div data-open={n.openByDefault}
data-coordx={n.config.coords[0]}
data-coordy={n.config.coords[1]}
data-enable-mobile-chords={n.config.useMobileCoords>
<a style={{
marginLeft: n.config.coords[0],
marginRight: n.config.coords[1]
}}
onClick={this.handleClick}>
<i className={`fa ${this.state.icon}`}></i>
</a>
<section data-is-open={this.state.isOpen} className="hotspot-detail">
<h2>{n.headline}</h2>
<p>{n.bodyCopy}</p>
</section>
</div>
);
return (
<section className="hotspots">
{console.log('status' + this.state.isOpen)}
<div>{items}</div>
</section>
)
}
}
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return <div name={this.props.name}
data-src-phone-highres={this.props.srcPhoneHighRes}
data-src-desktop={this.props.srcDesktop}
data-src-tablet-highres={this.props.srcTabletHighres}
data-src-phone={this.props.phone}
data-src-tablet={this.props.srcTablet}
data-src-desktop-highres={this.props.srcDesktopHighres}>
<HotSpots />
</div>
}
}
React.render( <App name={DATA.moduleName}
srcPhoneHighRes={DATA.images.desktop.externalUrl}
/>, app);
http://codepen.io/agriboz/pen/MyGOMG?editors=0010
Thanks in advance
As suggested by #XtremeCoder. You're not using the unique key identifier to trace your event. So in your react component pass the key in the click handler as follow onClick={this.handleClick.bind(this, i, n) Then include a new state i which can capture the current element clicked.
this.state = {
i: 0,
isOpen: false,
}
}
handleClick(i,n) {
this.setState({
i: i,
isOpen: !this.state.isOpen,
})
Now you can check for boolean on state change to display the content and change the icon
//for icon
<i className={`fa ${(this.state.isOpen && this.state.i==i ? 'fa-close' : 'fa-plus')}` }></i>
//for content
<section data-is-open={this.state.isOpen && this.state.i==i} className="hotspot-detail">
Here is the fully functioning code
let DATA = {
"module": {
"name": "E-Hot-PDP-X9405C-X9305C-Series-en_US-2015-03-M24-fullflush",
"masterName": "E-Hot-PDP-X9405C-X9305C-Series-en_GL-2015-03-M24-fullflush",
"maintenanceStructure": "Electronics/05_Televisions_and_Home_Cinema/01_Televisions/X9405C-X9305C-Series",
"_id": "urn:sony:module:editorial_hotspots:590359239320250140028",
"isLocalized": "false",
"masterId": "urn:sony:module:editorial_hotspots:1991426988117153303078371",
"version": "20160122080005",
"readyForLive": "No",
"mode": "full",
"editorialEnabled": "yes",
"type": "urn:sony:module:editorial_hotspots",
"createdAt": 1430303942648,
"updatedAt": 1453816025859,
"dependents": [
"urn:sony:media:image_family:1995667013507913303041209"
],
"config": {
"style": "dark-text-trans-box",
"background": "true",
"grouping": "group-middle",
"type": "full",
"variation": {
"background": "white"
},
"layout": {
"name": "full",
"text": "full",
"alignment": "center",
"columns": [
"12",
"0"
]
}
},
"hotSpots": {
"popups": [
{
"headline": "Full flush surface",
"alwaysOpen": "no",
"openByDefault": "no",
"bodyCopy": "With thin styling and a full flush screen surface, you’ll enjoy an immersive viewing experience.",
"dropShadow": "no",
"bullets": [],
"links": [],
"config": {
"size": "large",
"useMobileCoords": "no",
"hotSpotsIcon": "yes",
"dropShadow": "yes",
"coords": [
"10%",
"31%"
]
}
},
{
"headline": "Wedge",
"alwaysOpen": "no",
"openByDefault": "no",
"bodyCopy": "The unique shape has been designed to increase speaker capacity for a rich, full sound.",
"dropShadow": "no",
"bullets": [],
"links": [],
"config": {
"size": "large",
"useMobileCoords": "no",
"hotSpotsIcon": "yes",
"dropShadow": "yes",
"coords": [
"78.5%",
"49%"
]
}
}
],
"image": {
"type": "urn:sony:media:image_container",
"imageFamily": {
"name": "ImgF-PDP-X9405C-X9305C-Series-2015-03-M24-fullflush",
"masterName": "ImgF-PDP-X9405C-X9305C-Series-2015-03-M24-fullflush",
"maintenanceStructure": "Electronics/05_Televisions_and_Home_Cinema/01_Televisions/X9405C-X9305C-Series",
"_id": "urn:sony:media:image_family:1995667013507913303041209",
"isLocalized": "false",
"masterId": "urn:sony:media:image_family:1995667013507913303041209",
"localizedImageFamily": {
"name": "Loc-ImgF-PDP-XBR-X9405C-X9305C-Series-2015-03-M24-fullflush-SOLA",
"masterName": "Loc-ImgF-PDP-XBR-X9405C-X9305C-Series-2015-03-M24-fullflush-SOLA",
"maintenanceStructure": "Electronics/05_Televisions_and_Home_Cinema/01_Televisions/X9405C-X9305C-Series",
"_id": "urn:sony:localized_image_family:4512059944822603303579063",
"isLocalized": "false",
"masterId": "urn:sony:localized_image_family:4512059944822603303579063",
"locale": "all",
"version": "20150327064448",
"readyForLive": "No",
"type": "urn:sony:localized_image_family",
"createdAt": 1428930904784,
"updatedAt": 1428930906576,
"localizedImageFamilies": [
{
"imageFamily": {
"_id": "urn:sony:media:image_family:4492784185533253304288460",
"isLocalized": "false",
"locale": "all",
"maintenanceStructure": "Electronics/05_Televisions_and_Home_Cinema/01_Televisions/X9405C-X9305C-Series",
"masterId": "urn:sony:media:image_family:4492784185533253304288460",
"masterName": "ImgF-PDP-XBR-X9405C-X9305C-Series-2015-03-M24-fullflush-SOLA",
"name": "ImgF-PDP-XBR-X9405C-X9305C-Series-2015-03-M24-fullflush-SOLA",
"readyForLive": "No",
"type": "urn:sony:media:image_family",
"version": "20150327061216",
"createdAt": 1428930904949,
"updatedAt": 1428930906679,
"dependents": [],
"images": {
"desktop": {
"internalUrl": "https://prod-imgws.sony.eu/cms/images/Electronics/05_Televisions_and_Home_Cinema/01_Televisions/X9405C-X9305C-Series/E-Hot-PDP-X94-X93-en_GL-2015-03-M24-fullflush_XBR-desktop.jpg",
"md5": "90e6df273a7fe6ba9ef6411a83a995e3",
"mimeType": "image/jpg",
"status": "published",
"uploadAttempts": 1,
"uploadLastAttemptAt": 1428412207508,
"assetHandle": [
"a|23360418"
]
}
}
},
"locales": [
"es_AR",
"es_BO",
"pt_BR",
"es_CL",
"es_CO",
"es_CR",
"es_DO",
"es_EC",
"es_SV",
"es_GT",
"es_HN",
"es_MX",
"es_PA",
"es_PY",
"es_PE",
"es_PR",
"es_VE",
"es_NI"
]
}
],
"dependents": [
"urn:sony:media:image_family:4492784185533253304288460"
]
},
"locale": "all",
"version": "20150327075311",
"readyForLive": "No",
"type": "urn:sony:media:image_family",
"createdAt": 1428930904791,
"updatedAt": 1453731785919,
"dependents": [
"urn:sony:localized_image_family:4512059944822603303579063"
],
"images": {
"desktop": {
"internalUrl": "https://prod-imgws.sony.eu/cms/images/Electronics/05_Televisions_and_Home_Cinema/01_Televisions/X9405C-X9305C-Series/E-Hot-PDP-X94-X93-en_GL-2015-03-M24-fullflush_desktop.jpg",
"mimeType": "image/jpg",
"uploadAttempts": 1,
"uploadLastAttemptAt": 1427203801114,
"status": "published",
"md5": "93a8c28123eede1d42d33871e6553daf",
"assetHandle": [
"a|23104311"
],
"externalUrl": "//sonyglobal.scene7.com/is/image/gwtprod/93a8c28123eede1d42d33871e6553daf?fmt=jpeg"
},
"phone": {
"internalUrl": "https://prod-imgws.sony.eu/cms/images/Electronics/05_Televisions_and_Home_Cinema/01_Televisions/X9405C-X9305C-Series/E-Hot-PDP-X94-X93-en_GL-2015-03-M24-fullflush_phone.jpg",
"mimeType": "image/jpg",
"uploadAttempts": 1,
"uploadLastAttemptAt": 1427203801114,
"status": "published",
"md5": "40df54c624e5cebfa27fdae4d3207f5d",
"assetHandle": [
"a|23104300"
],
"externalUrl": "//sonyglobal.scene7.com/is/image/gwtprod/40df54c624e5cebfa27fdae4d3207f5d?fmt=jpeg"
},
"tablet": {
"internalUrl": "https://prod-imgws.sony.eu/cms/images/Electronics/05_Televisions_and_Home_Cinema/01_Televisions/X9405C-X9305C-Series/E-Hot-PDP-X94-X93-en_GL-2015-03-M24-fullflush_tablet.jpg",
"mimeType": "image/jpg",
"uploadAttempts": 1,
"uploadLastAttemptAt": 1427203801115,
"status": "published",
"md5": "5e9ab8ac405f170a5b24f1ccf1f71c1f",
"assetHandle": [
"a|23104304"
],
"externalUrl": "//sonyglobal.scene7.com/is/image/gwtprod/5e9ab8ac405f170a5b24f1ccf1f71c1f?fmt=jpeg"
}
}
}
}
},
"locale": "en_US",
"contentLocale": "en_US"
},
"moduleId": "editorial_hotspots_590359239320250140028",
"moduleName": "editorial_hotspots"
};
class HotSpots extends React.Component {
constructor(props, context) {
super(props, context);
//this.handleClick = this.handleClick.bind(this);
this.state = {
i: 0,
isOpen: false,
}
}
handleClick(i,n) {
this.setState({
i: i,
isOpen: !this.state.isOpen,
})
}
render() {
//console.log(DATA.module.hotSpots.popups);
let items = DATA.module
.hotSpots
.popups.map((n, i) =>
<div key={i}
onClick={this.handleClick.bind(this, i, n)} data-open={n.openByDefault}
data-coordx={n.config.coords[0]}
data-coordy={n.config.coords[1]}
data-enable-mobile-chords={n.config.useMobileCoords}>
<a style={{
marginLeft: n.config.coords[0],
marginRight: n.config.coords[1]
}}
>
<i className={`fa ${(this.state.isOpen && this.state.i==i ? 'fa-close' : 'fa-plus')}` }></i>
</a>
<section data-is-open={this.state.isOpen && this.state.i==i} className="hotspot-detail">
<h2>{n.headline}</h2>
<p>{n.bodyCopy}</p>
</section>
</div>
);
return (
<section className="hotspots">
{console.log('status' + this.state.isOpen)}
<div>{items}</div>
</section>
)
}
}
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return <div className={`${DATA.module.config.variation.background} ${DATA.module.config.type}`}
name={this.props.name}
data-src-desktop={this.props.srcDesktop}
data-src-phone={this.props.phone}
data-src-tablet={this.props.srcTablet}>
<HotSpots />
</div>
}
}
React.render( <App name={DATA.moduleName}
srcDesktop={DATA.module.hotSpots.image.imageFamily.images.desktop.externalUrl}
srcPhone={DATA.module.hotSpots.image.imageFamily.images.phone.externalUrl}
srcTablet={DATA.module.hotSpots.image.imageFamily.images.tablet.externalUrl}
/>, app);
Codepen:
http://codepen.io/anon/pen/WwJPLe?editors=0010
Hope it helps!
This is because you are associating same state(isOpen) to all the contents.
To show this modified your pen to this:
handleClick(event) {
this.setState({
parentClass :event.currentTarget.className,
isOpen: !this.state.isOpen,
icon: this.state.icon === 'fa-plus' ? 'fa-remove' : 'fa-plus'
})
}
render() {
let items = DATA.module
.hotSpots
.popups.map((n,i) =>
<div data-open={n.openByDefault}
data-coordx={n.config.coords[0]}
data-coordy={n.config.coords[1]}
data-enable-mobile-chords={n.config.useMobileCoords}>
<a style={{
marginLeft: n.config.coords[0],
marginRight: n.config.coords[1]
}}
onClick={this.handleClick} className={'parent'+i}>
<i className={`fa ${this.state.icon}`}></i>
</a>
<section data-is-open={this.state.isOpen && (this.state.parentClass===("parent"+i)) } className="hotspot-detail">
<h2>{n.headline}</h2>
<p>{n.bodyCopy}</p>
</section>
</div>
);
return (
<section className="hotspots">
{console.log('status' + this.state.isOpen)}
<div>{items}</div>
</section>
)
}
}
http://codepen.io/Debabrata89/pen/bpMOOM?editors=0010
Hope this helps