I have a problem with reading {find}.
The problem is in ProductDetail.js.
First, click on the products link than on any product to see details.
TypeError: Cannot read properties of null (reading 'find')
https://codesandbox.io/s/react-router-product-detail-pages-dynamic-links-forked-y1o0n?file=/src/ProductDetail.js:418-429
You've done some mistakes over there in your ProductDetail.js file.
First:
You can use useEffect hook to check and compare if there is a matching id or not.
Second:
You can use useState hook to store the thisProduct and update the thisProduct value by calling setThisProduct and use it in the JSXElement.
This is always a best practice to use the state for data set and get.
Here is more about React.Hooks
Third:
Price is a Object and you can't render your object like that, so use the key instead of object while rendering. like this: {thisProduct?.price?.current?.value}
You can learn more about optional chaining
Fourth:
productId which you're getting from useParams is a string type, and your productId from sneakers is a number type. So you need to change your productId to number while comparing like this: Number(productId)
Learn about Numbers in Js
Here is the complete code of yours:
// ProductDetail.js
import React, { useContext, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { StateContext } from "./GlobalContext";
function ProductDetail() {
const { productId } = useParams();
const { sneakers } = useContext(StateContext);
const [thisProduct, setThisProduct] = useState({});
useEffect(() => {
if (sneakers) {
const findProduct = sneakers.find((product) => {
return product.id === Number(productId);
});
console.log("findproduct", findProduct);
setThisProduct(findProduct);
}
}, [productId, sneakers]);
return (
<div>
{thisProduct && (
<>
<h1>{thisProduct?.name}</h1>
<p>Price: {thisProduct?.price?.current?.value}</p>
<p>{thisProduct?.description}</p>
</>
)}
</div>
);
}
export default ProductDetail;
completely check your state and props, it is not providing valid data to child component
<StateContext.Provider value={{ sneakers }}>
{console.log(sneakers, "== thisProduct")}
{children}
</StateContext.Provider>
console will show your data, it coming null so that is the issue
Related
I have a table in my database cold Apoet in my table field could name so I try to bring the name from the database but when I run the app this message appear
undefined is not an object (evaluating abc.name)
below my code
import React from 'react';
import {useEffect} from 'react';
import {useState} from 'react';
import { useRoute } from '#react-navigation/native';
import { DataStore } from 'aws-amplify';
import { Apoet } from '../../models';
const ProductScreen = () => {
const route = useRoute();
console.log(route);
const {abc, setABC}= useState<Apoet | undefined>(undefined);
useEffect (()=>{
if(!route.params?.id){
return ;
}
DataStore.query(Apoet,route.params.id).then(setABC)
},[route.params?.id]);
return (
<View>
<Text>{abc.name} </Text>
</View>
)}
Your useState is wrong, change with square brackets. Also be sure route changes as you expect otherwise it will be undefined again since your initial state value is also undefined
const [abc, setABC] = useState<Apoet | undefined>(undefined);
Issues
You've incorrectly accessed the return value from the useState hook, it returns an array, not an object.
const {abc, setABC} = useState<Apoet | undefined>(undefined);
You also use an undefined initial value, so attempting to access into it will throw an error.
Solution
Fix the return and use array destructuring assignment and use a valid initial value.
const [abc, setABC] = useState<Apoet>({});
...
<Text>{abc.name}</Text>
abc is now defined and can access a name property that will be undefined util the state is updated.
Or if you want to keep it allowed to be undefined, use a null check or Optional Chaining operator on the name property access.
Example:
const [abc, setABC] = useState<Apoet | undefined>();
...
<Text>{abc && abc.name}</Text>
or
<Text>{abc?.name}</Text>
I am fetching data from an API using axios.
On my invoice details page when I try to get data of only one invoice using this code
const id = props.match.params.id;
const invoice = useSelector((state) => state.invoices.find(invoice => invoice._id === id));
It returns an object or undefined but I only want an object inside an array or an empty array not undefined how should I do that?
When I tried to use .filter method instead of .find, it logged the array into the console infinite time.
Complete code:
import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import backIcon from '../assets/images/icon-arrow-left.svg'
import InvoiceDetailsHeader from './InvoiceDetailsHeader';
import { useSelector } from 'react-redux';
// remove this after adding DB
import data from '../data.json'
import InvoiceCardDetails from './InvoiceCardDetails';
const InvoiceDetails = (props) => {
const [invoiceData, setInvoiceData] = useState([]);
const id = props.match.params.id;
const invoice = useSelector((state) => state.invoices.find(invoice => invoice._id === id));
useEffect(() => {
setInvoiceData(invoice);
// console.log(invoiceData)
}, [id, invoice]);
return (
<div className="mx-auto px-12 py-16 w-full max-w-3xl">
<Link to="/" className="text-neutral text-xs"><img className="inline -mt-1 mr-4" src={backIcon} alt="back" /> Go back</Link>
<InvoiceDetailsHeader data={invoiceData} />
<InvoiceCardDetails data={invoiceData} />
</div>
)
}
export default InvoiceDetails
Anyone please help me with this.
I think it's because you're setting setInvoiceData(invoice) which is undefined at the very start. so make a check on it
if(invoice){
setInvoiceData([invoice])
}
please try this one
useEffect(() => {
if(invoice){
setInvoiceData([...invoiceData, invoice])
}
}, [id, invoice]);
First of all, I don't know if I missed anything, but I don't think it's a good way for invoice to be possible for both objects and empty array. I think a better way is to divide the conditions and render the invoice when the ID is not found.
If a filter method is used instead of a find, the filter method returns a new array instance each time. So as the second argument(invoice) of use Effect changes, the update callback of use Effect will continue to be repeated.
const invoice = useSelector((state) => state.invoices.find(invoice => invoice._id === id) ?? []);
What you want can be done simply using Nullish coalescing operator.
However, [] also creates a new array instance, so update callback is repeated indefinitely.
So to make what you want work in the current code, please remove the invoice from the dependence of useEffect as below.
useEffect(() => {
setInvoiceData(invoice);
// console.log(invoiceData)
}, [id]);
I have the following object being exported into another file:
info.js
export const info = {
companyName: '',
title: 'Some title',
year: '',
};
I'm importing this object into my Context.js like so:
InfoContext.js
import React, { useState, createContext } from 'react';
import {info} from './info'
export const InfoContext = createContext();
export const InfoProvider = ({ children }) => {
const [state, setState] = useState({
...info,
});
return (
<InfoContext.Provider value={[state, setState]}>
{children}
</InfoContext.Provider>
);
};
What I want to do is access the object values from my state inside my App.js. - Here is what I have tried but I am not having any success:
App.js
import React from "react";
import { InfoProvider, InfoContext } from "./InfoContext";
export default function App() {
return (
<InfoProvider>
<InfoContext.Consumer>
{state => (
<>
<h1>{state.title}</h1>
</>
)}
</InfoContext.Consumer>
</InfoProvider>
);
}
I'm clearly missing something obvious here. I've tried a few things but I'm not sure what the issue is. I feel it has something to do with my object being accessed from a separate file.
Additionally, here is a sandbox link with the above code. Any help would be greatly appreciated!
Code Sandbox
You are passing your value to Provider as array, but on Consumer you expecting it to be an Object.
You need to pass an Object instead:
<InfoContext.Provider value={{state, setState}}>
Also you are using Consumer wrong. As a callback it takes whole value that you've passed in Provider, not state:
<InfoContext.Consumer>
{(value) => (
<>
<h1>{value.state.title}</h1>
</>
)}
</InfoContext.Consumer>
or using destructured assignment:
<InfoContext.Consumer>
{({state}) => (
<>
<h1>{state.title}</h1>
</>
)}
</InfoContext.Consumer>
then you can use value.setState({...}) for example. etc. But note that this is a bad practice updating state like that.
Code Sandbox
I have this component, which works fine, but only once. If I reload, I have a undefined error.
My component :
import { UidContext } from "../components/AppContext"
import React, { useContext, useEffect, useState } from "react"
import axios from "axios"
export default function Balance() {
const uid = useContext(UidContext)
const [userWallet, setUserWallet] = useState(null)
useEffect(() => {
if (uid !== null) {
axios.get(`${process.env.REACT_APP_API_URL}api/balance/${uid}`)
.then((res) => setUserWallet(res.data[0]))
}
}, [uid])
return (
<section>
{userWallet.salaire1}
</section>
)
}
The result at first load :
userWallet result
and then, I have this error :
TypeError: Cannot read property 'salaire1' of null
Why would a console.log work, and not in the JSX ?
Thank you
You can use optional chaining with userWallet?.salaire1 . or handle with ternary conditional like
userWallet.salaire1 && userWallet.salaire1
In this case you have predefined state value userWallet is null. hence the UI couldn't render undefined property therefore your screen is blank and promp error
actually your console work but it the log return twice, the first one is null then the data shown.
I hope my answer can help to understand your issue :)
I'm passing a map to all my posts variable so that all my posts can appear as a single post, but it kept bringing up the error
I have tried solving it using the Reactjs Documentation from the react website, it shows almost the same code as mine.
import PropTypes from 'prop-types';
import PostItem from './PostItem';
class PostFeed extends Component {
render() {
const { posts } = this.props;
const list = posts.map((post) => <PostItem
key={post._id}
posts={post}
/>
);
return (
{list}
);
}
}
PostFeed.propTypes = {
posts: PropTypes.array.isRequired
};
export default PostFeed;
I expect every posts to appear as a single post from the postItem component
The error means, when your PostFeed mounts for first time at that time props are not available, so the error.
You can check if data available, like,
let list = "Loading...";
if(posts && posts.length > 0){
list = posts.map((post) => <PostItem key={post._id} posts={post} /> );
}
posts is probably result of an async action and its value is not available at the time that your function is doing its job. So it should have a default value or be checked before that has a value.do this:
if(this.props.posts && Array.isArray(this.props.posts) && this.props.posts.length > 0)
//then map