How to implement error boundaries in react with MERN? - reactjs

My goal is to simply dynamically present the data from a mongo database of a specific document.
const Details = () => {
const { id } = useParams()
const [product, setProduct] = useState(null)
useEffect(() => {
const fetchProduct = async () => {
const response = await fetch(`/api/products/${id}`)
const json = await response.json()
if (response.ok) {
setProduct(json)
console.log(json)
}
}
fetchProduct()
}, [id])
this code works fine as it gets the product, but my problem is occurring with the rendering:
return (
<div className="details">
<Menu />
<h1 className="movement">Product Details - {product.name}</h1>
</div>
);
}
the error that I'm getting is Uncaught TypeError: Cannot read properties of null (reading 'name') and to Consider adding an error boundary to your tree to customize error handling behavior.
my question being is how do i implement correct error handling

Just use the optional chaining operator:
product?.name
It's typical that in the first render product is not yet available so you cannot access to any of its properties. With the optional chaining operator you are covering this case.
See more: https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Operators/Optional_chaining
If you want to add Error Boundaries:
https://reactjs.org/docs/error-boundaries.html#gatsby-focus-wrapper

React renders the component before you make an api request, thus product doesn't exist (it's null based on how you set its initial value). Then when response is received you set product to state and react re-renders your component. Now product exists.
To solve it, render h1 only when product exist.
<div className="details">
<Menu />
{ product && <h1 className="movement">Product Details - {product.name}</h1> }
</div>
Also, you can render a message if product is not yet exist
<div className="details">
<Menu />
{ product && <h1 className="movement">Product Details - {product.name}</h1> }
{ !product && <p>Loading ... </p>
</div>

Related

React.js warning when iterating a list with map()

I got this warning from web debug console:
react-jsx-dev-runtime.development.js:87 Warning: Each child in a list should have a unique "key" prop.
Check the render method of App. See https://reactjs.org/link/warning-keys for more information. at div at App (http://localhost:3000/static/js/bundle.js:31:80)
Below is my code:
import './App.css';
import {ethers} from "ethers";
import { useState } from 'react';
function App() {
const [account, setAccount] = useState("")
const [data, setData] = useState([])
console.log(data);
const connect = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum)
let res = await provider.send("eth_requestAccounts", []);
setAccount(res[0]);
getData(res[0]);
}
const getData = (_account) => {
const options = {method: 'GET', headers: {accept: 'application/json'}};
fetch(
'https://api.opensea.io/api/v1/collections?asset_owner=0x3FB65FEEAB83bf60B0D1FfBC4217d2D97a35C8D4&offset=0&limit=3',
// `https://api.opensea.io/api/v1/collections?asset_owner=${_account}&offset=0&limit=3`,
options)
.then(response => response.json())
.then(response => {
setData(response);
console.log(response)})
.catch(err => console.error(err));
};
return (
<div className="App">
<button
onClick={connect}>
Connect
</button>
{data.map(nft => {
return(<div>
<img src={nft.featured_image_url} width="100px" height="100px"/>
<p>
{nft.discord_url}
</p>
<p>
{nft.banner_image_url}
</p>
</div>)
})}
<button
onClick={getData}>
GetData
</button>
</div>
);
}
export default App;
The code actually works as I expected. but when opening debug console from chrome I can see this warning pasted above.
Not sure why this warning? need some help, thank you
Googled this warning msg but cannot find useful info for this warning.
Is this warning a real issue or this can be ignored?
You need to add a key to your returned element, because React need to differentiate each elements.
You just need to add the parameter key to your block like:
{data.map(nft => (
<div key={nft.id}>
<img src={nft.featured_image_url} width="100px" height="100px"/>
<p>
{nft.discord_url}
</p>
<p>
{nft.banner_image_url}
</p>
</div>
))}
Why did I used nft.id ?
Most often, peoples use array map()'s index property as key, but it can be a bad practice depending on your goal.
using:
{data.map((nft, index) => (
<div key={index}>
...
))}
Works pretty fine, BUT in some cases (not that rare), when you perform edit action on your array's element, and you end up sorting them, React will be very confused.
Imagine you create an online music platform such as Spotify, and your API return you a list of your own playlist, ordered by name. The day you'll edit one playlist name, your entire playlist will have unwanted comportement because your element array's order will be modified, and the index you used as key.
So you may use map's index property as key, but be aware of what you need, it's generally better to use your element's id, uuid or other unique value as key.
See more on the official website
You must provide a unique key when you map with react.
Here is how :
{data.map((nft, index) => {
return(<div key={index}>
This is just an example. You can provide your own index.div key={nft} could work too if it is unique for each data.

How can I put dynamic category in react redux toolkit RTK?

Error is Cannot read properties of undefined (reading 'category')
I want to match my category on the product details page. But I can't able to implement it. I can't get the category I try a lot of ways. Please help me with how can I do it.
Redux says data: the actual response contents from the server. This field will be undefined until the response is received.
So how we can grab data? 🤔
Note: I use the Redux toolkit and RTK Query
I want to set my category chunk in my matchCategory state by default it is empty when the user click on the home page product button like ' View Detail' I want to match this id category and fetch all product to match the details category.
More Details
Remember I have a website My website has 20 product every product have two buttons one is add to cart and another one is View Details foe exmple You are a user you want to view product details before a product buy. Here is the main point I want to show a section that matches your product id category like electronics. You can call it a Related Product section :)
How can I do it?
My Product Details page
import React, { useEffect, useState } from "react";
import {
useGetSingleProductQuery,
useGetRelatedProductQuery,
useGetAllProductsQuery,
} from "../../features/productsApi";
import { useParams } from "react-router-dom";
import axios from "axios";
function ProductDetails() {
const { id } = useParams();
const {
data: getSingleProduct,
isFetching,
error,
} = useGetSingleProductQuery(id);
// Here I can't access category
const [matchCategory, setMatchCategory] = useState(getSingleProduct?.category);
const url = `http://localhost:5000/products?category=${matchCategory}`;
useEffect(() => {
axios
.get(url)
.then((res) => {
console.log(res.data)
setMatchCategory(res.data)
})
.catch((err) => console.log(err));
}, []);
return (
<div className="row mt-5">
{isFetching ? (
<p>Loading ...</p>
) : error ? (
<p>Error occured</p>
) : (
<>
<div className="col-md-6 mb-5 text-center">
<img
className="w-100"
style={{ height: "300px", objectFit: "contain" }}
src={getSingleProduct.image}
alt=""
/>
<div>{getSingleProduct.title.substring(0, 20)}</div>
<p>{getSingleProduct.description}</p>
// But here we access in jsx :( how ?
<p>{getSingleProduct.category}</p>
</div>
</>
)}
</div>
);
}
export default ProductDetails;
I think the data object is undefined in the first time so you need to add a fallback or add the ? like this:
const [matchCategory, setMatchCategory] = useState(getSingleProduct?.category);
// or const [matchCategory, setMatchCategory] = useState(getSingleProduct.category ?? {});

React, Cannot read properties of undefined Error with useEffect

I'm trying to get some weather data from an API, but I always get the same error of not being able to read properties of undefined. I've gone through different tutorials and previously asked issues, but I haven't been able to figure out what I'm doing wrong. Could anyone please give me a hand?
export default function Weather(){
const apiKey = process.env.REACT_APP_API_KEY
const weatherUrl = `http://api.weatherapi.com/v1/current.json?key=${apiKey}&q=Saxthorpe&aqi=no`
const [weatherData, setWeatherData] = useState();
const [error, setError] = useState(null);
useEffect(() => {
(
async function(){
try {
const response = await axios.get(weatherUrl);
setWeatherData(response.weatherData);
} catch (error) {
setError(error);
}
}
)();
}, [])
return (
<div className="weather-feature">
<h1>hi</h1>
<p className="location">{weatherData.location.name}</p>
<p className="temp">{weatherData.current.temp_c}</p>
<p className="weather-desc">{weatherData.current.condition.text}</p>
</div>
)
}
When pulling data like this and rendering components conditional on that data, you should account for situations in which the data is not yet available or null.
Specifically, you're attempting to render this data:
return (
<div className="weather-feature">
<h1>hi</h1>
<p className="location">{weatherData.location.name}</p>
<p className="temp">{weatherData.current.temp_c}</p>
<p className="weather-desc">{weatherData.current.condition.text}</p>
</div>
But it's not going to available on the first render (i.e. weatherData does not have a location property at first, since your default useState value is undefined).
There are many ways around this, and what you choose ultimately depends on your project and preferences.
You can use optional chaining as a simple protection against null references when checking nested properties:
return (
<div className="weather-feature">
<h1>hi</h1>
<p className="location">{weatherData.location?.name}</p>
<p className="temp">{weatherData.current?.temp_c}</p>
<p className="weather-desc">{weatherData.current?.condition?.text}</p>
</div>
Or you can return something else if weatherData is not ready. A good tool for this kind of thing is swr:
import useSWR from 'swr'
function Weather()
{
const { weatherData, error } = useSWR(weatherUrl, fetcher)
if (error) return <div>failed to load</div>
if (!weatherData) return <div>loading...</div>
return <div>hello {weatherData.location}!</div>
}
As a side note, another thing to consider is your useEffect dependencies:
useEffect(() => {
(
async function(){
try {
const response = await axios.get(weatherUrl);
setWeatherData(response.weatherData);
} catch (error) {
setError(error);
}
}
)();
}, [])
With an empty dependency array, your effect runs only on mount and unmount. If you want it to run based on some other variable(s) changing, add those variables to the dependency array.
You can debug to check the response. I think the respose is undefined from
const response = await axios.get(weatherUrl);
response = undefined => can not get weatherData property.
We are using useEffect you can debug on it by F12 in Chrome and see what happen and the reason of this bug. This is better than you come here to ask
Look: weatherData is your state, which is initially... nothing, because you don't pass any data.
So, you cannot access the location field on the first render because it does not exist yet.
It would help if you made sure weatherData exist:
return (
<div className="weather-feature">
<h1>hi</h1>
<p className="location">{weatherData?.location.name}</p>
<p className="temp">{weatherData?.current.temp_c}</p>
<p className="weather-desc">{weatherData?.current.condition.text}</p>
</div>
)

Having trouble with react error: 'Objects are not valid as a React child. If you meant to render a collection of children, use an array instead'

I am creating a React app with a Django backend and using Redux to maintain state. I am trying to render data in a child component and I keep getting this error: Objects are not valid as a React child (found: object with keys {id, sets, reps, weight, bridge_id}). If you meant to render a collection of children, use an array instead.
What I have is a main screen RoutineScreen.js which is supposed to render child components displaying the exercises in the routine and sets, reps, weight for each.
This is what my code looks like:
function RoutineScreen() {
// Get routine id which is passed in through program screen components
const { id } = useParams()
const routine_id = id
//Extract program id from routine
const state = {...store.getState()}
let program_id
// -- set routines, which we will loop through to find program id
const routines = state.programRoutines.routines
// -- loop through routines, find match for routine_id
for (let i in routines) {
if (i.id == routine_id){
program_id = i.program
}
}
const dispatch = useDispatch()
const routineExercises = useSelector(state => state.routineExercises)
const { error, loading, exercises } = routineExercises
useEffect(() => {
dispatch(listRoutineExercises(program_id, routine_id))
}, [dispatch])
return (
<div className="screen-container">
<Header/>
<SearchBar/>
<div className="card-container">
{exercises.map((exercise, index) => (
// Render Routines
<Exercise key={exercise.id} routine_id={routine_id} index={index} exercise={exercise}/>
))}
</div>
</div>
)
}
export default RoutineScreen
function Exercise({ exercise, routine_id, index }) {
// Get routine id which was passed in through program screen components
const { id } = useParams()
const exercise_id = id
const dispatch = useDispatch()
// use spread operator to unpack elements from exerciseParameters
const exerciseParameters = useSelector(state => state.exerciseParameters)
const { error, loading, parameters } = exerciseParameters
useEffect(() => {
dispatch(listExerciseParams(routine_id, exercise_id))
}, [dispatch])
return (
<div style={{ textDecoration: 'none' }}>
<div>
<h3>{exercise.name} </h3>
<h4>{parameters[index].reps}</h4>
<h4>{parameters[index].weight}</h4>
</div>
</div>
)
}
export default Exercise
Here is my data:
Note: I am completely new to React and Redux so feel free to let me know if there are also any other suggestions for how I should fix my code or if I am missing any relevant information for this problem. Thanks!
I tried to access the specific elements in the object by using parameters[index].reps, expecting that it would then display that data, but instead I received the error above.
I think you can do something like :
return(
<div style={{ textDecoration: 'none' }}>
<div>
<h3>{exercise.name} </h3>
{parameters.map(parameter => (
<>
<h4>{parameter.reps}</h4>
<h4>{parameter.weight}</h4>
</>
))}
</div>
</div>
)

Needs Help To Troubleshoot Fetching Single Document From Firebase Database As Detailed Page

I'm try to get single document as detail information from Firebase database under collection "books", however my array method map does not recognize as function due to the render produce "undefined". Somehow render again and produce the object value in log. I posted the screenshot of the log above, hoping somebody help me out, thanks!!!!!
import React, {useState, useEffect} from 'react'
import {Link} from 'react-router-dom'
import firebase from '../config/fbConfig'
const BookDetails = (props) => {
const [books, setBooks] = useState([])
useEffect(() => {
const db = firebase.firestore()
const id = props.match.params.id
var docRef = db.collection("books").doc(id);
docRef.get().then(doc => {
if(doc.exists){
const data = doc.data()
console.log("Document data:", data)
setBooks(data)
}else {
console.log("No such document!");
}
}).catch(error => {
console.log("Error getting document:", error);
})
}, [])
console.log('this log is before return', books.title)
return (
<div className="book_details">
<Link to="/"><h2>Home</h2></Link>
{console.log("this log is in the return method", books.title)}
<h1>The Summary Of the Book </h1>
{books.map( book => <ul key = "book.id" >
<li>Book Title: {book.title}</li>
<li>Book Author: {book.author}</li>
<li>Book Summery: {book.brief}</li>
</ul>)}
</div>
)
}
export default BookDetails
Because you are testing whether books is undefined and only call the map function if it is defined (i.e. {books && books.map( [...] )}), the problem must lie somewhere else.
You are fetching a single document from your Firebase database. Therefore, the returned data will not be an array but an object which does not have the map function in its prototype. You can verify this from your console logs.
Your component renders twice because you are changing its state inside the useEffect via setBooks(data).
const db = firebase.firestore()
const id = props.match.params.id
First of all move these lines inside of useEffect.
Coming to the problem
You are fetching a single doc(object) from firebase and saving it in a state which is an array. Change your useState to
const \[book, setBook\] = useState(undefined) // or useState({})
Change your return to
return (
<div className="book_details">
<Link to="/"><h2>Home</h2></Link>
{console.log("this log is in the return method", books.title)}
<h1>The Summary Of the Book </h1>
{book && <div key={book.id}> {book.brief} </div>}
</div>
)
// or {Object.keys(book).length !== 0 && <div key={book.id}> {book.brief} </div>}
if you have used empty object in useState.

Resources