I am developing a basic quiz app in react. I have a QuestionList component which sends question,options to another component Question as props. Inside the Question component while trying the following code
<div>
{props.question}
</div>
<div>
{props.options.map(option=>{return(
<div> <button onClick={()=> submitAnswer(option.isCorrect)} >{option.option}</button> </div>)
})}
</div>
It is giving me an error that Cannot read property map of unknown.
But when I remove the second div, the question is rendered and after that if I paste the second div and save it the options are also rendered.
If I refresh the page in browser the error is getting repeated. Why is this weird behavior
EDIT
options is an array of objects
options:[
{option:"Hyderabad",isCorrect:false},
{ option:"Delhi",isCorrect:true},
]
Parent component
import React, { useEffect, useState } from 'react';
import Question from "./Question"
const QuestionList=()=>{
const [questions,setQuestions]=useState([{}]);
useEffect(()=>{
async function fetchQuestions(){
const response=await fetch("http://localhost:7000/questions");
const data=await response.json();
setQuestions(data.questions);
console.log(data.questions)
}
fetchQuestions();
console.log(questions)
},[])// eslint-disable-line react-hooks/exhaustive-deps
const [currentQuestion,setCurrentQuestion]=useState(0);
const [score,setScore]=useState(0);
const [showScore,setShowScore]=useState(false);
const handleOptionCorrect=(isCorrect)=>{
if(isCorrect)
{
setScore(score+1)
}
const nextQuestion=currentQuestion+1;
if(nextQuestion<questions.length){
setCurrentQuestion(nextQuestion)
}
else{
setShowScore(true)
}
}
return(
<div>
{showScore?(
<div>
Your score is {score} / {questions.length}
</div>
):(
<>
<Question question={questions[currentQuestion].question}
options={questions[currentQuestion].options}
handleOptionCorrect={handleOptionCorrect}/>
</>
)}
</div>
);
}
export default QuestionList;
Child Component
import React from 'react'
const Question=(props)=>{
const submitAnswer=(isCorrect)=>{
props.handleOptionCorrect(isCorrect)
}// eslint-disable-next-line
return(
<>
<div>
{props.question}
<div>
{props.options.map(option=>{return(
<div> <button onClick={()=> submitAnswer(option.isCorrect)} >{option.option}</button> </div>)
})}
</div>
</div>
</>
);
}
export default Question
Resolved
Previously I was taking options directly from props. After looking at #Naren 's answer I got an idea how to take options array from props and edited my code accordingly and it worked.
Thank you!
Maybe QuestionList receiving props(question, options) as undefined. Better to add fallbacks. Please add more code to your question to get better understanding.
const QuestionList = (props) => {
const { question, options = [] } = props
if (!question) return <span>loading question...</span>
....
}
Issue
Your initial questions state is an array with an empty object but your are selecting the the first element and sending an undefined options value.
const [questions, setQuestions] = useState([{}]);
const [currentQuestion, setCurrentQuestion] = useState(0);
<Question
question={questions[currentQuestion].question}
options={questions[currentQuestion].options} // <-- options is undefined
handleOptionCorrect={handleOptionCorrect}
/>
Child - throws error because props.options is undefined.
{props.options.map(option=>{return(
<div>
<button onClick={()=> submitAnswer(option.isCorrect)}>
{option.option}
</button>
</div>)
})}
Solution
You should provide valid initial state that doesn't blow up your UI.
const [questions, setQuestions] = useState([{ options: [] }]); // <-- provide options array
const [currentQuestion, setCurrentQuestion] = useState(0);
<Question
question={questions[currentQuestion].question}
options={questions[currentQuestion].options} // <-- options is defined
handleOptionCorrect={handleOptionCorrect}
/>
Suggestion
If Question component is rendered by other components then it's good to provide a fallback value in case you forget to always provide valid props.
const Question = ({ handleOptionCorrect, options = [], question })=>{
const submitAnswer=(isCorrect)=>{
handleOptionCorrect(isCorrect)
}
return (
<>
<div>
{question}
<div>
{options.map(option=> (
<div>
<button onClick={()=> submitAnswer(option.isCorrect)} >
{option.option}
</button>
</div>)
)}
</>
);
}
Related
I am new to learning React. The issue here is, my AAPost component gets rendered before I am setting the value of posts constant.
I tried to use async & await earlier but it wasn't helping at all. There must be something I am doing wrong, could someone point me in the right direction? Thank you :)
import React, { useEffect, useState } from 'react'
import './AAFeed.css'
import AAPost from './AAPost'
import AATweetBox from './AATweetBox'
import { db } from '../fbr'
function AAFeed() {
const [posts, setPosts] = useState([]);
useEffect(()=>{
db.collection('posts').onSnapshot((snapshot) => {
setPosts(snapshot.docs.map(doc => doc.data()));
});
},[])
return (
<>
<div className='feed'>
{/* Header */}
<div className='feed__feedtitle'>
<h2>Home Page</h2>
</div>
{/* Tweet Box */}
<AATweetBox />
{/* Tweets */}
{posts.forEach((post) => {
console.log('pp',post);
<AAPost
displayName={post.displayName}
username={post.username}
verified={post.verified}
timestamp={post.timestamp}
text={post.text}
image={post.image}
avatar={post.avatar}
/>
})
}
</div>
</>
)
}
export default AAFeed
I think mummo is because you are using forEach
forEach does not return a value
{ posts.map((post) => {
console.log('pp',post);
return(<AAPost
displayName={post.displayName}
username={post.username}
verified={post.verified}
timestamp={post.timestamp}
text={post.text}
image={post.image}
avatar={post.avatar}
/>) })}
Visit this site for information.
add this
before post.foreach and test it
if(post !== []){ post.foreach... }}
I am new to react and have started it 2 days ago, so if anyone can help me with this please.
FetchApi is a function in another file that makes a fetch promise to an api and returns the value.
But the problem is when I try to render the data and supply it as props to ImagesList component it gives Ojects are not valid error.
But seems to work fine when use the snippet.
So can somebody help me with rendering my data in a correct manner
Also the snippet contains data in json format and also the FetchApi when I checked in the console. So my question is why does it not give me the same results as the snippet if both the returned values are json array.
import ReactDOM from 'react-dom';
import FetchApi from './imagesfile'
import {imagesList} from './spareFile'
console.log(imagesList)
const ImagesList = (props) => {
console.log(props)
return (
<section>
<div>
<ul>
<li>{props.img}</li>
</ul>
</div>
</section>
)
}
// Snippet
// {imagesList.map((item) => {
// const {id, img} = item;
// return <ImagesList key={item.id} {...item} />
// })}
console.log(FetchApi().then(data => {console.log(data)}))
const Images = () => {
console.log('hello')
return(
<section>
<div>
{FetchApi().then(data=>{
{data.map((item) => {
const { id, alt_description, urls } = item;
return <ImagesList key = {item.id} {...item}/>
})}
})
}
</div>
<h3>
'Hello'
</h3>
</section>
)
}
ReactDOM.render(<Images />, document.getElementById('root'))```
Please need help.
Your FetchApi() inside of the <div> is technically a Promise. As an example, what your app is attempting to render is equivalent to:
return <section>
<div>
{new Promise(/*some promise*/)}
</div>
</section>
You need to hold an empty array in state and update the component's state once the fetch has completed. For example:
import ReactDOM, {useState, useEffect} from 'react-dom';
//...
const Images = () => {
// this useState will initially set the value of items to an empty array
const [items, setItems] = useState([])
// this useEffect will be called once when the component mounts,
// fetch the data then call `setItems` setting the state for items,
// so it can be rendered with data below.
useEffect(() => {
FetchApi().then(data => {
setItems(data)
})
}, [])
return(
<section>
<div>
{
items.map((item) => {
return <ImagesList key = {item.id} {...item}/>
})
}
</div>
<h3>
'Hello'
</h3>
</section>
)
}
import React, { useState } from 'react'
export default function App() {
const [todos , set_todos] = useState([''])
const [input , set_input] = useState('')
const new_todo = (event) =>{
set_todos = ([...todos,input]);
}
return (
<>
<h1>hello world</h1>
let input = <input value={input} onChange={event=> set_input(event.target.value)}/>
<button onClick ={new_todo}>add todo</button>
<ul>
{todos.map(todo =>(
<li>{todo}</li>
))}
</ul>
</>
)
}
the error is in 7th line of the code
i am a totally new beginner so it would be helpful if you explain it to me
If you want to update your state (that is an array) in react hooks, you should try like this :
const new_todo = (input) => set_todos(oldState => [...oldState,input])
with this code, you will not see any error but I have some offer for your code that make it better:
put your inputs such as input and buttons in the form tag
use variable declarations outside of return ( let return be for HTML tag and its better to use your logics outside of return ) make it easier to read
and I think your code can be like this:
import React, { useState } from 'react'
export default function App() {
const [todos , set_todos] = useState([''])
const submitTask = (e) =>{
// Calling preventDefault() during any stage of event flow cancels the event,
// meaning that any default action normally taken by the implementation
// as a result of the event will not occur
e.preventDefault();
const { taskInput } = e.target; // get your input by name
// assign input value to your state
set_todos(oldState => [...oldState, taskInput.value ])
}
return (
<>
<h1>hello world</h1>
<form onSubmit={submitTask}>
<input name="taskInput" />
<button type="submit">add todo</button>
</form>
<ul>
{todos.map(todo =>(
<li>{todo}</li>
))}
</ul>
</>
)
}
You should use set_todos like below:
set_todos([...todos, input]);
Because set_todos is a function. You can find the State Hook's usage in Introducing Hooks.
I am trying to build a React Recipe App. I am making API request for Recipe Search.
My question is about using map in this line
{recipes.map((recipe) => (
<Recipe />
))}
We are trying to access the objects inside of the array. Right? Yes, this works. However, I don't get why
{recipes.forEach ((recipe) => (
<Recipe />
))}
doesn't work. Could you please explain? The whole code is below.
App.js
import React, { useEffect, useState } from "react";
import Recipe from "./Recipe";
import "./App.css";
function App() {
const APP_ID = "xxxxxxxxxxx";
const APP_KEY = "yyyyyyyyyyyyyyyyyyy";
const [recipes, setRecipes] = useState([]);
useEffect(() => {
getRecipes();
}, []);
//yeni bir fonksiyon yazıyorum. take care of fetching all data
const getRecipes = async () => {
const response = await fetch(
`https://api.edamam.com/search?q=chicken&app_id=${APP_ID}&app_key=${APP_KEY}`
);
const data = await response.json();
setRecipes(data.hits);
};
return (
<div className="App">
<form className="search-form">
<input className="search-bar" type="text" />
<button className="search-button" type="submit">
Search
</button>
</form>
{recipes.map((recipe) => (
<Recipe />
))}
</div>
);
}
export default App;
Recipe.js
import React from "react";
const Recipe = () => {
return (
<div>
<h1>Title</h1>
<p>Calories</p>
<img src="" alt="" />
</div>
);
};
export default Recipe;
Because .map() returns the ARRAY of the <Recipe /> entities, and .forEach() doesn't return ANYTHING. React does not render by side-effect - it needs the JSX returned.
The forEach version doesn’t return an array of rendered elements like the map version does. The elements rendered inside the forEach don’t exist outside the scope of the forEach function, whereas the map version returns the elements in an array.
I created a form with a state attached to it , but after every character input the form looses focus.
i realize its because the state cause the form to re render how do i escape this?
import axios from 'axios'
const App = () => {
const [countries,setCountries] =useState([])
const [ newName,setnewName ] = useState('')
useEffect(()=>{
axios.get('https://restcountries.eu/rest/v2/all')
.then(response=>{
setCountries(response.data)
})
},[])
const handleChange = (event) =>{
setnewName(event.target.value)
}
const Finder = ()=>{
return(
<div>
<form>
<div>
Find country : <input value={newName} onChange={handleChange} />
</div>
</form>
</div>
)
}
return(
<div>
<p>Meow</p>
<Finder/>
</div>
)
}
export default App```
As you are rendering Finder as a component and it will be create a new function on each and every render instead as you are rendering it inside a component invoke it as a function like below
try changing the return statement as
return(
<div>
<p>Meow</p>
{Finder()}
</div>
)
Please go through this sandbox for reference