Cannot read values from array object from the map function in React - reactjs

I am trying to pass value from an array items object to another component using map(). All values is coming from api call and is showed in console.But when passing the value from here to Titlecard, I am getting error cannot read properties of undefined map()?I have given the code.Also I have shared Titlecard. Always passing only one record into the array Can anyone guide me here? Thanks
import axios from "axios";
import React, { useEffect, useState } from "react";
import { Container } from "react-bootstrap";
import Titlecard from "./Titlecard";
import { HeroesUrl } from "../apiurl/HeroesApi";
const TitleHero = () => {
const [items, setItems] = useState([]);
useEffect(() => {
axios.get(HeroesUrl).then((response) => {
setItems(response.data);
console.log(response.data);
});
}, []);
return (
<>
<Container>
<div>
{items.map((item) => {
return (
<>
<Titlecard key={item.id} item={item} />
</>
);
})}
</div>
</Container>
</>
);
};
export default TitleHero;
import React, { useEffect, useState } from "react";
const Titlecard = (item) => {
return (
<>
<div> item.firstName </div>
</>
);
};
export default Titlecard;

I edit my answer after I saw you shared Titlecard component.
There are 2 problems.
The first is in your return, it should be:
<div>{item.firstName}</div>
Because what you return before is just a string like "item.firstName".
Second, you have to make a destructuring to the props in Titlecard like:
const Titlecard = ({item}) => {...}
or return:
<div>{item.item.firstName}</div>
The first one is your props name, the second is the prop you pass.
So, with using destructuring Titlecard should be like this:
import React from "react";
const Titlecard = ({item}) => {
return (
<>
<div>{item.firstName}</div>
</>
);
};
export default Titlecard;
Please share Titlecard component code.
It's look like that there is a part in the Titlecard component that use the item from the map. In the first time before the axios call finished, the prop item is still empty array, so if you use in the Titlecard component item.something you will get an undefined error.
One solution is to add a loader that initial to true, and after the axios call finished set it to false, so if the loader is true, render a loader, else render your map code.
Another solution is adding ? when you use item in Titlecard component, like: item?.something, what means only if item is not null or undefined.

Related

ReactJS - PropTypes not validating

PropTypes is not validationg (ignore!!!) this simple component, I tried everything but it doesn't work.
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import Item from "./Item";
import chunkArray from "../../utils/chunkArray";
const AllItems = (props) => {
const { items, title } = props;
const [arrayToMap, setArrayToMap] = useState([]);
// set size array based on columns
const arrSize = 12;
// chunk array
useEffect(() => {
setArrayToMap(chunkArray(items, arrSize));
}, [items]);
return (
<>
{arrayToMap.length === 0 ? (
<div>Wait</div>
) : (
<>
{title}
{arrayToMap.map((x) => {
return (
<div className="row">
{x.map((y, i) => (
<div className="col-1" key={i}>
<Item item={y} size={"small"} />
</div>
))}
</div>
);
})}
</>
)}
</>
);
};
AllItems.propTypes = {
items: PropTypes.array.isRequired,
title: PropTypes.string.isRequired,
};
export default AllItems;
I can pass whatever to ìtems or title but doesn't stop the render.
Any idea?
Thanks!
rerendering your component is not about proptypes, it's because you set state in your useEffect function without any condition. so the component will work in an infinite loop of rendering. you can read about it here
you need to add some condition to set your state in once like this :
useEffect(() => {
if(arrayToMap.length === 0 && items.length > 0){
setArrayToMap(chunkArray(items, arrSize));
}
}, [items]);
so if your state is clear and your items array is not empty so the condition will work.
The reason you are not seeing the warnings, Is because you're using props as the param in your component. Change your component to the following
const AllItems = ({ items, title }) => {}
Proptypes doesn't stop component from rendering but you can use eslint to validate the params and stop the app from starting. Add eslintrc.json file to your project and add the following rule.
"react/prop-types": 2,

List Rendering in React JS

import React,{useState} from 'react'
import App from './App'
const AppList = ()=>{
const [arr,addElement] = useState([1])
const handleClick = ()=>{
arr.push(1)
addElement(arr)
}
return(
<>
{
arr.map((elements)=>{
return <h1>{elements}</h1>
})
}
<button onClick={handleClick}>add</button>
</>
)
}
export default AppList
I tried to add elements and render. the items are getting added in array but its not getting rendered in browser.
React is checking state updates by reference, since you provide the same array reference the component is not going to re-render.
Instead, make an immutable update
const AppList = () => {
const [arr, addElement] = useState([1])
const handleClick = () => {
addElement(prevState => [...prevState, 1])
}
return (
<>
{arr.map(element => (
<h1>{element}</h1>
))}
<button onClick={handleClick}>add</button>
</>
)
}
You also need to provide a unique key when rendering arrays, but don't use index as key since it can cause problems when adding and removing items from the array. You don't have anything unique here so you will need to think of a way of solving it. You can find more info about it here
to rerender somthing in React you need to change the state,
the best way to do it is to pass NEW array to the state, Thats why
you have the setState method.
So, you just need:
import React,{useState} from 'react'
import App from './App'
const AppList = ()=>{
const [arr,setArray] = useState([1])
const handleClick = ()=>{
let newArr = [...arr,1]
setArray(newArr)//setArray will be a better name.
}
return(
<>
{
arr.map((elements)=>{
return <h1>{elements}</h1>
})
}
<button onClick={handleClick}>add</button>
</>
)
}
export default AppList

how to set value in hooks

I have a problem with hooks in ReactJS
as you see here i defined a prop that should call from child component
but when i want to change the value by calling change component it doesn't work and my state doesn't set.
can someone help me?
don't forget to read the comments
import React, {useState} from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
const setSelectedCollection = (e) => {
setCollection(e);
console.log(e); // returns the true value
console.log(collection); // returns null
}
return(
<div>
<Collection onChoosed={(e) => setSelectedCollection(e)}/>
</div>
)
}
export default SingleReminderPage;
Use setState with a callback function
const setSelectedCollection = (e) => {
setCollection((state)=> {...state, e});
}
setCollection(e) - wont update the state immediately.
I want to Understand SetState and Prevstate in ReactJS
This might help you around, the useEffect will be called on each colletion update
import React, { useState, useEffect } from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
useEffect(() => {
console.log(collection)
}, [collection])
return (
<div>
<Collection onChoosed={(e) => setCollection(e)} />
</div>
)
}
export default SingleReminderPage;
it seems like the setCollection is called after the logging action to check something like that you can print the collection value on the component itself
import React, {useState} from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
const setSelectedCollection = (e) => {
setCollection(e);
console.log(e); // returns the true value
console.log(collection); // returns null
}
return(
<div>
{collection}
<Collection onChoosed={(e) => setSelectedCollection(e)}/>
</div>
)
}
export default SingleReminderPage;

How do I get my React like button to increment when not liked, and then decrement when liked?

The state variable likes just continues to increment. I thought I have my onSelect function alternating the boolean value of liked, but apparently not.
Here's the error:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls
setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested
updates to prevent infinite loops.
Here's App.js:
import React, { useState } from 'react';
import './App.css';
import LikeButton from './components/LikeButton';
function App() {
const [likes, updateLikes] = useState(23);
const [liked, updateLiked] = useState(false);
return (
<LikeButton
secret='like-button'
numLikes={likes}
liked={liked}
// status={liked}
onSelect={function clickLike(liked) { //This function is happening, but the else block
// never fires.
if (liked) {
updateLikes(likes + 1);
} else { updateLikes(likes - 1)
};
updateLiked(!liked);
}
}
/>
// onClick function here, or in LikeButton.js?
);
}
export default App;
And here's LikeButton.js:
import React from 'react';
import { FaThumbsUp } from 'react-icons/fa';
export default function LikeButton({secret, liked, numLikes, onSelect}) {
return (
<>
<FaThumbsUp />
<div key={secret} liked={liked} onClick={onSelect(liked)}>Like Button</div>
<p>{numLikes}</p>
</>
);
}
Issue
You invoke your onClick callback immediately, which updates state in the parent.
import React from 'react';
import { FaThumbsUp } from 'react-icons/fa';
export default function LikeButton({secret, liked, numLikes, onSelect}) {
return (
<>
<FaThumbsUp />
<div
key={secret}
liked={liked}
onClick={onSelect(liked)} // invoked right away
>
Like Button
</div>
<p>{numLikes}</p>
</>
);
}
Solution
Make it an anonymous inner function.
import React from 'react';
import { FaThumbsUp } from 'react-icons/fa';
export default function LikeButton({secret, liked, numLikes, onSelect}) {
return (
<>
<FaThumbsUp />
<div
key={secret}
liked={liked}
onClick={() => onSelect(liked)} // invoked when clicked
>
Like Button
</div>
<p>{numLikes}</p>
</>
);
}
Suggestion
You should also really use functional state updates when the next state values depend on current state values, i.e. when incrementing/decrementing counts or toggling a boolean. I also suggest to move the updateLikes into an useEffect hook to react to liked being toggled. This may make the logic about incrementing/decrementing a little clearer.
function App() {
const [likes, updateLikes] = useState(23);
const [liked, updateLiked] = useState(false);
useEffect(() => {
updateLikes(likes => likes + (liked ? 1 : -1));
}, [liked]);
return (
<LikeButton
secret='like-button'
numLikes={likes}
liked={liked}
// status={liked}
onSelect={function clickLike(liked) {
updateLiked(liked => !liked);
}}
/>
);
}

On click returns null instead of an object

It's really basic I guess. I'm trying to add onClick callback to my script & I believe I'm missing a value that would be responsible for finding the actual item.
Main script
import React from 'react';
import { CSVLink } from 'react-csv';
import { data } from 'constants/data';
import GetAppIcon from '#material-ui/icons/GetApp';
import PropTypes from 'prop-types';
const handleClick = (callback) => {
callback(callback);
};
const DownloadData = (props) => {
const { callback } = props;
return (
<>
<CSVLink
data={data}
onClick={() => handleClick(callback)}
>
<GetAppIcon />
</CSVLink>
</>
);
};
DownloadData.propTypes = {
callback: PropTypes.func.isRequired,
};
export default DownloadData;
Storybook code
import React from 'react';
import DownloadData from 'common/components/DownloadData';
import { data } from 'constants/data';
import { action } from '#storybook/addon-actions';
export default {
title: 'DownloadData',
component: DownloadData,
};
export const download = () => (
<DownloadData
data={data}
callback={action('icon-clicked')}
/>
);
So right now with this code on click in the storybook I'd get null and I'm looking for an object.
One of the potential issues I can see is that your handleClick function is stored as it is in-memory, when you import the component. That means you're keeping reference of something that doesn't exists and expects to use it when rendering the component with the callback prop.
Each instance of a component should have its own function. To fix it, move the function declaration inside the component. Like this:
const Foo = ({ callback }) => {
// handleClick needs to be inside here
const handleClick = callback => {
console.log("clicked");
callback(callback);
};
return <div onClick={() => handleClick(callback)}>Click me!</div>;
};
Check this example.
If this doesn't fix your problem, then there is something wrong with how you're implementing Storybook. Like a missing context.

Resources