Right way to parse Redux state props - using super json objects - reactjs

Following my trip with React and Redux, I'm facing a problem, simple in appearance, but hard to solve : I'm setting my Redux state with a very big JSON object. Those datas are retrieved from an async call. When I'm setting it, I created an entry in my reducer with
let initialState = {
pages: []
}
Then, I'm putting different pages, with their params and datas into this pagesarray. So far so good, the state is well updated.
BUT, my different app pages use only parts of it as you can imagine. For instance, I have a page named Gallery which might need my state to look like this :
"pages": [
{
"component":"Gallery",
"key": "gallery",
"title": "Galerie photos",
"url": "/galerie-photos",
"sections": [
{
"component": "Gallery",
"params": {
"images": [
{
"id": 1,
"src": "http://lorempicsum.com/futurama/627/300/1",
"alt": "alternate text"
},
{
"id": 2,
"src": "http://lorempicsum.com/futurama/500/400/2",
"alt": "alternate text"
},
{
"id": 3,
"src": "http://lorempicsum.com/futurama/400/320/3",
"alt": "alternate text"
},
{
"id": 4,
"src": "http://lorempicsum.com/futurama/800/500/4",
"alt": "alternate text"
},
{
"id": 5,
"src": "http://lorempicsum.com/futurama/320/300/5",
"alt": "alternate text"
},
{
"id": 6,
"src": "http://lorempicsum.com/futurama/420/360/6",
"alt": "alternate text"
}
]
}
}
]
}
]
In my GalleryContainer, I'm requesting only the images property as this is the only concern of my Gallery component. I'm able to retrieve this data with no problem. Testing with the following code returns the desired images array :
But as far as I tested this, the images are not retrieved by my Gallerycomponent : when console logging on the component side, I got undefined, and on the container side, no problem.
I tested something different : I set a galleryImages property via the reducer, and set it with the gallery images directly. This worked.
The questions are : Why doesn't it work in the first case, and do in the second ? Do I have to work only with datas that are set as state properties only ? Can't I have a "super" json set in my state to work with directly ?
Thanks to light me up on this one :)
// Component
import React from 'react'
const Gallery = ({images}) => (
<div>
{images.map((image, key) =>
<div key={key} className="image-element-class" style={card}>
<img src={image.src} alt={image.alt} style={imageStyle}/>
</div>
)}
</div>
)
const card = {
width: 'calc((100% / 3) - (15px / 1.5))',
marginBottom: '15px',
boxSizing: 'border-box'
}
const imageStyle = {
width: '100%'
}
export default Gallery
// Container
import { connect } from 'react-redux'
import Gallery from '../../components/pages/Gallery'
const getImages = state => {
return {
images: state.data.pages.filter(page => page.component === 'Gallery' &&(
page.sections.filter(section => section.component === 'Gallery' &&(
section.params.images
))
)),
}
}
const mapStateToProps = state => {
return getImages(state)
}
const GalleryContainer = connect(
mapStateToProps,
{}
)(Gallery)
export default GalleryContainer
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>

Related

Building quiz site with react, I have 4 buttons that's an option of the question answer

I mapped the component which generated 5 questions and obviously 20 buttons. My problem now is that the first 4 buttons on each row always have the same ID when I try to get each button clicked so I can check if the correct answer was selected. I need every button to have a unique ID. I've tried different ID functions but none seem to work, I think the problem is from my logic and not the ID itself.
import React from "react";
export default function Question_page(props) {
const [triviaApi, setTriviaApi] = React.useState()
const styles = {
backgroundColor: props.isHeld ? "#59E391" : "grey"
}
return (
<div className="main-question-container">
<div className="question">
<h3>{props.my_items.question}</h3>
</div>
<div className="question-button">
<button onClick={props.holdAnswer}>{props.my_items.incorrect_answers[0]}</button>
<button onClick={props.holdAnswer}> {props.my_items.incorrect_answers[1]}</button>
<button onClick={props.holdAnswer}>{props.my_items.correct_answer}</button>
<button onClick={props.holdAnswer}>{props.my_items.incorrect_answers[2]}</button>
</div>
</div>
)
}
The App components below
import QuestionPage from "./components/Question_page";
import React from "react";
import {nanoid} from 'nanoid'
import {BrowserRouter as Router, Route, Routes} from "react-router-dom"
import JsonData from "./JsonData";
export default function App() {
const [colorState, setColorState] = React.useState({
isClicked: true
})
const jsonDataElement = JsonData.map(item => {
return item.results.map((innerItem,index) => {
const myID = Math.random()
return (
<QuestionPage
key={myID}
my_items={innerItem}
isHeld={innerItem.isClicked}
holdAnswer={() => holdAnswer(myID)}
/>
)
})
})
function holdAnswer(ID) {
// here is where I want to check if every bottom has a different ID so I can change the colour and save the variable to check if the answer was right during my "check answer" click. The issue is, that the first button on the row gives me the same ID, I need to get a different ID on every button which contains the answer option so I can run this function
console.log(ID)
}
return (
<Router>
<NavBar/>
<Routes>
<Route path="/" element={<Start/>}/>
<Route path="/question" element={
<div>
{jsonDataElement}
<div className="check-answer-container">
<button className="check-answer">Check answer</button>
</div>
<hr/>
</div>
}/>}/>
</Routes>
</Router>
);
}
The JSON data im using
export default [{
"response_code": 0,
"results": [
{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "medium",
"question": "Which of these is the name for the failed key escrow device introduced by the National Security Agency in 1993?",
"correct_answer": "Clipper Chip",
"incorrect_answers": [
"Enigma Machine",
"Skipjack",
"Nautilus"
]
},
{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "medium",
"question": "In the server hosting industry IaaS stands for...",
"correct_answer": "Infrastructure as a Service",
"incorrect_answers": [
"Internet as a Service",
"Internet and a Server",
"Infrastructure as a Server"
]
},
{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "medium",
"question": ".rs is the top-level domain for what country?",
"correct_answer": "Serbia",
"incorrect_answers": [
"Romania",
"Russia",
"Rwanda"
]
},
{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "medium",
"question": "What was the first Android version specifically optimized for tablets?",
"correct_answer": "Honeycomb",
"incorrect_answers": [
"Eclair",
"Froyo",
"Marshmellow"
]
},
{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "medium",
"question": "The name of technology company HP stands for what?",
"correct_answer": "Hewlett-Packard",
"incorrect_answers": [
"Howard Packmann",
"Husker-Pollosk",
"Hellman-Pohl"
]
}
]
}]
You're only setting myID once right now so all buttons are receiving the same ID - you need to set it for each button instead of once for the whole QuestionPage.
You could probably make a function on Question_page that returns Math.random and then call that from id={} on each button

making dynamic route by API with next+react

i'd like to make a dynamic route with react and next.js
I have an API like this.
[
{
"id": 1,
"name": "books",
"pages": "277",
"title": "Book A",
"no": 88
},
{
"id": 2,
"name": "books",
"pages": "90",
"title": "Book B",
"no": 32
},
{
"id": 4,
"name": "books",
"pages": "184",
"title": "Book C",
"no": 89
}
...
]
and this API will show in the screen like this. this is mapped li in Book.jsx
<li>
<p>{book.name}</p>
<p>{book.pages}</p>
</li>
Now, what i want to make is when i click li, it connects to the "no" of API and make a new window.
so I created [bookNo].jsx and the same location with pages > index.jsx and added these to [bookNo].jsx
const DetailedView = () => {
const router = useRouter();
const {bookNo} = router.query;
return (
<div>
<p>{bookId}</p>
</div>
);
};
to make router works, i added these code to Book.jsx
useEffect(() => {
getBooks().then(res => {
const book = res.data.DATA.RESPONSE.BOOKS;
for (let i = 0; i < book.length; i++) {
const bookNo = bookData[i].no;
}
}).catch(e => {
console.log(e)
})
}, [])
<Link href="pages/bookView/[bookId]" as={`/bookView/${bookId}`}>
<li onClick>
<p>{book.name}</p>
<p>{book.pages}</p>
</li>
</Link>
And I've got 2 errors that
ReferenceError: bookNo is not defined and router is not working at all.
Can anyone help me with this problem?

React reading nested JSON data fails when loads or refresh

I am developing a screen to show data based on search results. Below is the JSON output.
When I click search result below component is called. In the result screen i need id, name and table1, table2 data.
Table1 and Table2 are nested outputs and these will be displayed in React Table or Data Grid (next step)
Issue: Unable to render StudyData.table1.name
Options Tried
1. UseEffect()
UseEffect() able to read StudyData.studyname but not StudyData.table1.name
Assigned to a variable
Assigned to a state
2. Render
tried using map for subdocuments
Finding: It fails only first time load and refresh. I tried to comment -> save->load -> remove the comments and save. Then works (as component is loaded). I am missing something during the first time load or refresh. Please help
[
{
"id": "DD3",
"studydate": "DDD",
"studydescription": "DD3 Description",
"studyname": "DD3",
"table1": [
{
"no": "1",
"name": "DD3 Name",
"date": "Krishna",
"description\r": "1111\r"
},
{
"no": "2",
"name": "DD3 Nam2",
"date": "Test2",
"description\r": "2222\r"
},
{
"no": "3",
"name": "DD3 Name3",
"date": "Test3",
"description\r": "3333"
}
],
"table2": [
{
"No": "2",
"Study Field1": "21",
"Study Field2": "22",
"Study Field3\r": "23"
}
],
"table3": [
{
"No": "3",
"Study Field5": "T31",
"Study Field6": "T32",
"Study Field7": "T33",
"Study Field 8\r": "T34"
}
],
"_rid": "QeNcANZFTTIKAAAAAAAAAA==",
"_self": "dbs/QeNcAA==/colls/QeNcANZFTTI=/docs/QeNcANZFTTIKAAAAAAAAAA==/",
"_etag": "\"33002e92-0000-0200-0000-5fa6fe320000\"",
"_attachments": "attachments/",
"_ts": 1604779570
}
]
COMPONENT CODE
import React, { useEffect } from "react";
import api from "./UploadStudyFilesapi";
import { DataGrid } from "#material-ui/data-grid";
export default function StudyDisplay(props) {
let myData = props.Study;
const [StudyData, setStudyData] = React.useState([]);
const [Table1Rows, setTable1Rows] = React.useState([]);
useEffect(() => {
// Update the document title using the browser API
api.getStudy(myData.studyname).then((json) => setStudyData(json));
console.log(StudyData.studyname); //works
//console.log(StudyData.table1.no) //doesn't work
let myTable = StudyData.table1;
console.log(myTable);
setTable1Rows(StudyData.table1);
console.log(Table1Rows);
}, [myData]);
return (
<div>
{StudyData.length === 0 ? (
<h1>Loading...</h1>
) : (
StudyData.map((item) => (
<div key={item.studyname}>
{item.studyname}
{/* DOESNT WORK first brose or refresh*/}
{item.table1.map((key2) => (
<div>
{console.log(key2.name)}
{key2.name}
{/* Want to pass Key2 to DataGrid or React-Table */}
{/* <DataGrid rows={key2} columns={{field1:"No"}} /> */}
</div>
))}
</div>
))
)}
</div>
);
}
Thanks for your support. I added conditional rendering based on search selection in parent component. That resolved all the issues

How do I iterate over a JSON file in react?

I'm new to React and for the life of me cannot figure this out.
I have a JSON file (Fontawesome icons):
{
"icons": [
{
"name": "Glass",
"id": "glass",
"unicode": "f000",
"created": 1,
"filter": [
"martini",
"drink",
"bar",
"alcohol",
"liquor"
],
"categories": [
"Web Application Icons"
]
},
{
"name": "Music",
"id": "music",
"unicode": "f001",
"created": 1,
"filter": [
"note",
"sound"
],
"categories": [
"Web Application Icons"
]
},
// etc
To start with I just want to return the name of each icon.
I've been trying to follow various tutorials and have:
import React, { PureComponent } from "react";
import iconList from './services/iconList';
export default class App extends PureComponent {
render() {
const items = iconList.map(data=>{
return(
<div>
<ul>
<li>
<span>{data.name}</span>
</li>
</ul>
</div>
)
})
return items;
}
}
But I get the error: .map is not a function.
I'm not sure what I can do differently. Each tutorial I see seems to use the map function. Is there a better/different way?
Try using
const items = iconList.icons.map(data=>{
Your data is an object with an icons property in it. You can also destructure your iconList when you import:
import {icons as iconList } from './services/iconList';

"TypeError: Cannot read property 'name' of undefined" when looping on JSON

I have a JSON like this :
{
"cards": [
{
"name": "aquaman",
"img": "aquaman.png"
},
{
"name": "batman",
"img": "batman.png"
},
{
"name": "captainamerica",
"img": "captainamerica.png"
},
{
"name": "deadpool",
"img": "deadpool.png"
},
{
"name": "flash",
"img": "flash.png"
},
{
"name": "greenlantern",
"img": "greenlantern.png"
},
{
"name": "ironman",
"img": "ironman.png"
},
{
"name": "spiderman",
"img": "spiderman.png"
},
{
"name": "ironfist",
"img": "ironfist.png"
},
{
"name": "thepunisher",
"img": "thepunisher.png"
},
{
"name": "wonderwoman",
"img": "wonderwoman.png"
},
{
"name": "xman",
"img": "xman.png"
}
]
}
and I want to loop on these objects and to render a div with a backgroundImage with the img property of every object,
I tried this, but it tells me:
TypeError: Cannot read property 'name' of undefined
import React, { useEffect, useState } from "react";
import cardsJson from "../../../utils/cards.json";
const [cards, setCards] = useState();
useEffect(() => {
setCards(cardsJson.cards);
}, [cards]);
{cards &&
cards.map((card: any, index: number) => (
<div key={card.name} className="cardHero">
<div className="front"></div>
<div
className="back"
style={{
backgroundImage: `url(${require(`../../../assets/images/${card.img}`)})`
}}
></div>
</div>
))}
It looks like inside the map I can't get name or img for every card ?? And when I console log "cards", it show me the JSON like above with name and img properties
Assuming your code is wrapped in a function component (see below), it may be an issue with how you're importing / using cards.json. Also in the useEffect, make the dependency array [] instead of [cards]. I changed your code to this and it works:
import React, { useEffect, useState } from "react";
import cardsJson from "./cards.json";
export function Cards() {
const [cards, setCards] = useState();
useEffect(() => {
console.log(cardsJson);
setCards(cardsJson.cards);
}, []);
return (
<div>
{
cards &&
cards.map((card, index) => (
<div key={card.name} className="cardHero">
<div className="front">{card.name}</div>
<div
className="back"
></div>
</div>
))
}
</div>
);
}
Note I put the cards.json in the same folder as the Cards component, I'm not outputting the image and I updated the dependency array.
If you only want to display the cards there is no reason for them to be inside a useEffect. I recommend you to create a constant and initialize it with cardsJson.cards, like:
const cards = cardsJson.cards;
Or if you want to play with states, just initialize your state with cardsJson.cards, like:
const [cards, setCards] = useState(cardsJson.cards);

Resources