I am a beginner learning ReactJs.
I am trying to express data from firebase.
when I put {quizes[0].quiz} this was working.
But, if I want to use 'qno' variable, what should I do?
import {useParams} from "react-router-dom";
import {dbService} from 'fbase';
const QuizPlay = () => {
const {cateId} = useParams();
const [qno, setQno] = useState(0);
const [quizes, setQuizes] = useState([]);
useEffect(() => {
dbService
.collection("quizes")
.where('cateId','==',cateId)
.orderBy("createdAt", "desc")
.onSnapshot((snapshot) => {
const quizArray = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setQuizes(quizArray);
})
}, [cateId]);
return (
<div className="container">
{quizes[qno].quiz} <=== error
</div>
)
}
export default QuizPlay;
Before quizes has been populated quizes[qno] - (quizes[0]) - is undefined and therefore does not have a quiz property.
Try
<div className="container">
{quizes[qno]?.quiz}
</div>
assuming your compiler supports optional chaining, or if not:
<div className="container">
{quizes.length > 0 && quizes[qno].quiz}
</div>
I think it's happening because when the component is mounted the first time the quizzes haven't been loaded yet. So quizes is empty. Try this:
{quizes && quizes[qno].quiz}
Or
{quizes.length > 0 && quizes[qno].quiz}
Could you tell us exactly what error your getting?
Related
I made my basic ToDo list site after learning react. I wanted to add a function of saving the items on reload. I am a beginner in react so I am facing difficulty in this. I tried the following code:
import React from "react";
import ToDoList from "./components/ToDoList";
import Navbar from './components/Navbar'
import '../src/App.css'
export default function TodoInput() {
const saveLocalTasks = () => {
let savedTasks = localStorage.getItem('tasks')
console.log(savedTasks)
if (savedTasks) {
return JSON.parse(localStorage.getItem('tasks'))
} else {
return []
}
}
const [task, setTask] = React.useState('')
const [count, setCount] = React.useState(0)
const [taskList, setTaskList] = React.useState([saveLocalTasks()])
const [disable, setDisable] = React.useState(true)
const [viewTaskList, setViewTaskList] = React.useState(true)
const updateTaskList = () => {
setTaskList([...taskList, {object: task, key: Date.now()}])
setTask('')
setViewTaskList(false)
setCount(count + 1)
setDisable(true)
}
const inputValue = e => {
setTask(e.target.value)
e.target.value === '' || task === '' || task.length === 0
?
setDisable(true)
:
setDisable(false)
}
// console.log(task.length)
React.useEffect(() => {
localStorage.setItem('tasks', JSON.stringify(taskList.object))
}, [taskList])
return (
<div>
<Navbar />
<header>
<div className="todolist-border">
<div className="todo-input-form">
<input
className = "inputText"
placeholder="Add a Task"
value={task}
onChange = {inputValue}
/>
<button disabled = {disable} onClick = {updateTaskList} className="todo-add-button">+</button>
</div>
{
viewTaskList || count === 0
?
<div className="pendingTasks-div">
<img className = "pending-task-image"
src= "https://dm0qx8t0i9gc9.cloudfront.net/watermarks/image/rDtN98Qoishumwih/task-pending-cartoon-business-vector-illustrations_zJCs81OO_SB_PM.jpg"
alt="pending-tasks" />
<p className="no-task-message">There are no pending tasks!! #Enjoy🥳🥳</p>
</div>
:
<ToDoList count = {count} setCount = {setCount} task = {task} taskList = {taskList} setTaskList = {setTaskList}/>
}
</div>
</header>
</div>
)
}
But the following error is coming up:
The following is the code for ToDoList component:
import React from "react";
export default function ToDoList(props) {
const deleteTaskListItem = (key) => {
const updatedList = props.taskList.filter((item) => {
return (
item.key !== key
)
})
props.setTaskList(updatedList)
props.setCount(props.count - 1)
}
return(
<div>
{props.taskList.map((item) => {
return (
<div key = {item.key} className="todolist-div">
<input type="checkbox" className="list-checkbox">
</input>
<p>{item.object}</p>
<button onClick={()=>deleteTaskListItem(item.key)} className="delete-button">X</button>
</div>
)
})}
</div>
)
}
Kindly suggest a method to add this feature.
The above error happens when you try to JSON.parse undefined. Check this link. Here, I tried to do some changes in your code on CodeSandbox. There you can find some changes I have made.
Firstly, you shouldn't try to set data in this useState const [taskList, setTaskList] = React.useState([saveLocalTasks()]). You should set data in useEffect.
In the following code, you are trying to save taskList.object but taskList is an array. The below code will throw an error.
React.useEffect(() => {
localStorage.setItem('tasks', JSON.stringify(taskList.object))
}, [taskList])
As you asked in your question, you want to try to save data when the user reloads the window. You can achieve this by using window.onbeforeunload event (line 48).
Hope the above will help you.
P.S: The codesandbox code I shared isn't fully functional. I have made just some changes that will help you to go ahead with your coding. Thank you.
This is the structure of the json being fetched. I am trying to render some of the nested threads data to a web page with react.
import react, {useState, useEffect} from "react";
import axios from 'axios'
import ReactJson from 'react-json-view'
const FeaturedBoards = () => {
const [boards, setBoards] = useState([{page: '', threads: {}}]);
useEffect(() => {
fetchBoards();
}, []);
const fetchBoards = () => {
axios.get('https://a.4cdn.org/po/catalog.json')
.then((res) => {
console.log(res.data);
setBoards(res.data);
})
.catch((err) => {
console.log(err);
});
};
if(boards === 0) {
return <div>Loading...</div>;
}
else{
return (
<div>
<h1>Featured Boards</h1>
<div className='item-container'>
{boards.map((board) => (
<div className='board' key={board.id}>
<p>{board['threads']}</p>
</div>
))}
</div>
</div>
);
}
};
export default FeaturedBoards;
I have tried everything to display some of the nested threads data but nothing comes up. I've tried doing a second call to map on board but no luck, storing it in a variable and calling from that still nothing. Am I doing something totally wrong?
I believe this is more fully answered by How can I access and process nested objects, arrays or JSON?. but to explain for this particular data structure, keep reading.
Look at your actual data... boards is an array. Each element in it is an object with page (int) and threads (array) properties. Each threads array element is an object with other properties. You can use map to iterate arrays and return a JSX representation of the objects within.
For example
const [boards, setBoards] = useState([]); // start with an empty array
const [loading, setLoading] = useState(true)
useEffect(() => {
fetchBoards().then(() => setLoading(false))
}, []);
const fetchBoards = async () => {
const { data } = await axios.get('https://a.4cdn.org/po/catalog.json')
setBoards(data)
}
return loading ? <div>Loading...</div> : (
<div>
<h1>Featured Boards</h1>
<div className="item-container">
{boards.map(board => (
<div className="board" key={board.page}> <!-- 👈 note "page", not "id" -->
{board.threads.map(thread => (
<p>{thread.name}</p>
<p>{thread.sub}</p>
<p>{thread.com}</p>
<!-- etc -->
))}
</div>
))}
</div>
</div>
)
I'm trying to navigate an array of orders stored in each "User". I am able to query and find ones that have orders but I'm not able to display them. I keep getting an error "Cannot read property 'map' of null". Where am I going wrong?
The image below shows how all the orders are stored in "order"
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { firestore } from "../../../FireBase/FireBase";
const OrdersAdmin = (props) => {
const [order, setOrder] = useState(null);
useEffect(() => {
const fetchOrder = async () => {
const doc = await firestore.collection("Users");
const snapshot = await doc.where("orders", "!=", []).get();
if (snapshot.empty) {
console.log("No matching documents.");
return <h1>No Orders</h1>;
}
var ans = [];
snapshot.forEach((doc) => {
console.log(doc.id, "=>", doc.data().orders);
setOrder(doc.data().orders)
});
};
fetchOrder();
}, [props]);
return (
<div className="adminOrders">
<h1>orders</h1>
{console.log(order)}
{order.map((orderItem) => (
<div className="singleOrder" key={orderItem.id}>
<p>{orderItem}</p>
</div>
))}
</div>
);
};
export default OrdersAdmin;
The issue is that the initial value of order is null. null does not have Array.prototype.map, therefore you get the error. Try updating your render to use conditional rendering to only attempt Array.prototype.map when order is truthy and an Array:
{order && order.length > 0 && order.map((orderItem) => (
<div className="singleOrder" key={orderItem.id}>
<p>{orderItem}</p>
</div>
))}
Otherwise you can use a better default value of an empty array for order which would have Array.prototype.map available to execute:
const [order, setOrder] = useState([]);
Hopefully that helps!
i don't know how make this guys, i can't update my state with the api array, and if i put it in useEffect i have an error cause i am not sending any data, help me please is my first time using stackoverflow
import React, { useEffect, useState } from "react";
import getTeam from "../Helpers/getTeam";
const selectTeams = [
"Barcelona",
"Real Madrid",
"Juventus",
"Milan",
"Liverpool",
"Arsenal",
];
const Select = () => {
const [team, setTeam] = useState(null);
const [loading, setLoading] = useState(null);
const handleOption = async (e) => {
setLoading(true);
let teamsJson = await getTeam(e.target.value);
let arr = [];
Object.keys(teamsJson).map((teamjs, i) => {
return arr.push(teamsJson[teamjs]);
});
console.log(arr);
console.log(team);
setTeam(arr);
setLoading(false);
};
return (
<div
style={{ background: "skyblue", textAlign: "center", padding: "20px" }}
>
<h1>Equipos Disponibles</h1>
<div>
<select onChange={handleOption}>
<option>Elige tu equipo</option>
{selectTeams.map((selectTeam, i) => {
return <option key={i}>{selectTeam}</option>;
})}
</select>
</div>
{loading ? <h1>suave</h1> : (
team !== null ? (
team.map((newTeam, i) => {
return (
<div>
the items are here
</div>
)
})
) : null
)}
</div>
);
};
export default Select;
i let you my api file down
const getTeam = async (teamName) => {
const url = `https://www.thesportsdb.com/api/v1/json/1/searchteams.php?t=${teamName}`;
const res = await fetch(url);
const team = await res.json();
return team;
};
export default getTeam;
i wanna update my const team with the response of my api call, but it doesn't update it, i dont know what do, please help me
The teamsJson value is an object with a single key and value of some array
{ teams: [...] }
So you are updating your state with a nested array when you push the value into another array.
let arr = [];
Object.keys(teamsJson).map((teamjs, i) => {
return arr.push(teamsJson[teamjs]);
});
Based upon how you want to map your team state array I assume you just want the raw inner array from teamJson.
const { teams } = await getTeam(e.target.value);
setTeam(teams);
Then when you are mapping you can access any of the properties you need.
team.map((newTeam, i) => {
return <div key={i}>{newTeam.idTeam}</div>;
})
I've just tested it & it seems to works just fine.
The only 2 issues seem to be that:
You don't use team anywhere (apart from a console.log statement).
At the moment when you console.log(team); the constant team will (yet) be null for the first time (because it still keeps the initial state).
Here's what I see in React dev tools after picking a random team in the <select>:
I've retrieved a list of categories using an API. Now I want to fetch images from an URL based on the categories. I tried using each category to fetch images from another API, but I'm not sure how to do it.
import React, { useEffect, useState } from 'react';
import './css/Category.css';
function Category() {
useEffect(() => {
fetchData();
getImage();
}, []);
const [categories, setCategories] = useState([]);
const [image, setImage] = useState('');
const fetchData = async () => {
const data = await fetch('https://opentdb.com/api_category.php')
const categories = await data.json();
console.log(categories.trivia_categories)
setCategories(categories.trivia_categories)
}
const getImage = async (name) => {
console.log(name)
const q = name.split(' ').join('+')
const img = await fetch(`https://pixabay.com/api/?key=apikey&q=${q}&image_type=photo`)
const image = await img.json();
console.log(image)
setImage(image.previewURL)
}
return (
<div className="categories">
Yesss
<div className="category-grid">
{categories.map(category => (
<div className="category">
{category.name}
<img src={getImage(category.name)} /> //do not know what to do here to fetch image of the respective category
</div>
))}
</div>
</div>
)
}
export default Category;
After changes suggested by Noah, I was able to show only one image.
const getImage = async (name) => {
const query = stringMan(name.name)
console.log(query)
const img = await fetch(`https://pixabay.com/api/?key=17160673-fd37d255ded620179ba954ce0&q=${query}&image_type=photo`)
const image = await img.json();
console.log(image)
setImage({ [name.name]: image.hits[0].largeImageURL })
}
return (
<div className="categories">
Yesss
<div className="category-grid">
{categories.map(category => (
<div className="category" key={category.id}>
{category.name}
<img key={category.id} src={image[category.name]} />
</div>
))}
</div>
</div>
)
There are a couple of changes that you can make here.
One issue that I see is that you have a single image variable, that's being re-used for every single category. So when you map over a list of categories (for example let's say we have categories: [history, science, and math]). The current code will call getImage three times, with history, science, and math as parameters.
However, there is only one state variable that is being written to. Which means the last execution of setImage is the only one that will be preserved.
So, you might want to change image from being the URL of a category image, to an object that has the shape:
{
history: [url],
science: [url],
math: [url]
}
The other change to make is that you are calling the getImage() function directly in the rendered output <img src={getImage(category.name)} />. Instead, this should simply use the value that was assigned to the image state: <img src={image} />.
To actually fetch the image, you can use the useEffect hook (https://reactjs.org/docs/hooks-effect.html) to react to changes to the categories variable. That might look something like:
useEffect(() => {
categories.forEach((c) => getImage(c));
}, [categories]);
The useEffect hook will invoke the function it is given, whenever the dependencies change. This will allow you to trigger the getImage function in response to changes to the categories.
There're lot of improvement that could be done as stated by #noah-callaway above/below but coming straight to the point you need to simply fix the URI creation logic to use encodeURIComponent like below:
import React, { useEffect, useState } from 'react';
function Category() {
useEffect(() => {
fetchData();
getImage();
}, []);
const [categories, setCategories] = useState([]);
const [image, setImage] = useState('');
const fetchData = async () => {
const data = await fetch('https://opentdb.com/api_category.php')
const categories = await data.json();
console.log(categories.trivia_categories)
setCategories(categories.trivia_categories)
}
const getImage = async (name) => {
return encodeURI(`https://pixabay.com/api/?key=apikey&q=${encodeURIComponent(name)}&image_type=photo`)
}
return (
<div className="categories">
Yesss
<div className="category-grid">
{categories.map(category => (
<div className="category">
{category.name}
<img src={getImage(category.name)} />
</div>
))}
</div>
</div>
)
}
don't have the api key so can't test but it'll give you something like
https://pixabay.com/api/?key=apikey&q=Entertainment%3A%20Comics&image_type=photo
good luck, hope it works.