Wrong rendering when using map to render from an array in React - javascript

In this issue, I have an array of objects like:
const sampleCards = [
{userName:'user1',avatarURL:'../avatar/role11.jpg',mainPageURL:'/',isFollowed:true},
{userName:'user2',avatarURL:'../avatar/role12.jpg',mainPageURL:'/',isFollowed:true},
{userName:'user3',avatarURL:'../avatar/role13.jpg',mainPageURL:'/',isFollowed:false},
];
then I used map() to render this array:
export default function SearchBar(){
return (
<div className='searchBar'>
{sampleCards.map((result)=>{
return (
<SearchResultCard result={result}/>
);
})}
</div>
);
}
A SearchResultCard component be like:
export default function SearchResultCard(result){
const [isFollowed,setIsFollowed] = useState(result.isFollowed);
const handleFollowingClicked = ()=>{
setIsFollowed(false);
};
const handleFollowClicked=()=>{
setIsFollowed(true);
};
useEffect(()=>{console.log(result.userName)});
return (
<div className="search-result-card">
<Link to={result.mainPageURL}>
<div className="search-result-card-left">
<img src={result.avatarURL} alt={result.userName} className="search-result-img"/>
<p>{result.userName}</p>
</div>
</Link>
{isFollowed ? (
<button className="following-button" onClick={handleFollowingClicked}>Following</button>
):(
<button className="follow-button" onClick={handleFollowClicked}>Follow</button>
)}
</div>
);
}
The problem is that values in result seem not been passed to so it just ends with a 'Follow' button while other elements are not rendered

The parameter in SearchResultCard is the entire props object. This has a property called result. You need to destructure to get the result prop:
function SearchResultCard({ result }){ ... }
Here's a snippet:
const {useState} = React;
const sampleCards = [
{ userName:'user1', isFollowed:true },
{ userName:'user2', isFollowed:true },
{ userName:'user3', isFollowed:false },
];
function SearchResultCard({ result }){
const [isFollowed,setIsFollowed] = useState(result.isFollowed);
const handleFollowingClicked = _ => setIsFollowed(false);
const handleFollowClicked= _ => setIsFollowed(true);
return (
<div className="search-result-card">
{isFollowed
? ( <button onClick={handleFollowingClicked}>Following</button>)
: ( <button onClick={handleFollowClicked}>Follow</button>)
}
</div>
);
}
function Example(){
return (
<div className='searchBar'>
{sampleCards.map((result)=>{
return (
<SearchResultCard key={result.userName} result={result}/>
);
})}
</div>
);
}
// Render it
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Example />
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>

Related

useState hook is breaking activePost as useEffect is triggered by selectedPost

My goal was to fetch posts from Graphcms and populate an array - posts, and populate it into postlist, then the main component will change according to what the user clicks on a post from postlist, I can see the posts array is populated , but when i click on a post on postlist I get the following error
Main.js:22 Uncaught TypeError: Cannot read properties of undefined (reading 'featuredImage')
Below my files
App.js
function App() {
const [selectedPost,setSelectedPost] = useState(0);
const [posts, setPosts] = useState([]);
useEffect(() => {
const fetchPosts = async () => {
const { posts } = await request(
'https://api-ap-southeast-2.graphcms.com/v2/ckxo1np9m5kw601xpccps4lrn/master',
`
{
posts {
id
title
slug
excerpt
featuredImage
{
url
}
}
}
`
);
console.log("print posts " , posts)
setPosts(posts);
};
fetchPosts();
}, []);
return ( <div className='app'>
<Header/>
{
posts.length>0 && (<>
<Main posts={posts} selectedPost={selectedPost}/>
<PostList posts={posts} setSelectedPost={setSelectedPost} />
</>
)
}
</div>
)
}
export default App;
And the Main.js Component
const Main = ({selectedPost,posts}) => {
const[activePost,setActivePost] =useState(posts[0])
console.log("activePost ", activePost)
useEffect(()=>{
setActivePost(posts[selectedPost])
},[posts,selectedPost])
return (
<div className='main'>
<div className='mainContent'>
<div className='postHighlight'>
<div className='postContainer'>
<img
className='selectedPost'
src= {activePost.featuredImage.url}
alt=''
/>
</div>
</div>
<div className='postDetails' style={{color:'#fff'}}>
<div className='title'>
{activePost.title} </div>
<span className='itemNumber'></span>
<span className='postExcerpt'>{activePost.excerpt}</span>
</div>
<div className='otherDetails'>
</div>
</div>
</div>
)
}
export default Main
And then we have postList.js file
const PostList = ({posts,setSelectedPost}) => {
return (
<div className='postList'>
{posts.map(post=>(
<div onClick={()=>setSelectedPost(post.id)}>
<CollectionCard key={post.slug} title={post.title} excerpt={post.excerpt} imageSrc={post.featuredImage.url}/>
</div>
)) })
</div>
)
}
export default PostList
Based on your app, you are using the index of the selected post.
The error arises from your onclick function. You are passing post.id to setSelectedPost() so you are accessing the posts array incorrectly. Hence, the undefined.
Just use the current index on your map function:
<div className='postList'>
{posts.map((post, index) => (
<div onClick={() => setSelectedPost(index)} key={post.slug}>
<CollectionCard
title={post.title}
excerpt={post.excerpt}
imageSrc={post.featuredImage.url}
/>
</div>
))
}
</div>

How to pass a property from a react component to its parent?

I am creating a "presentation" component with multiple sections, each rendered dynamically.
In the parent component which houses all the different children, I want the "next button" disabled for each part until a certain condition has been met. The button lives in the parent component.
This component does not pass the property:
Child one example:
export function ChildOne() {
const [condition, setCondition] = useState(false);
return (
<div>
<button onClick={() => setCondition(true)}>
hello world
</button>
</div>
);
}
Parent:
import ChildOne, condition from "../child-one"
export default function Parent() {
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
I'm not sure how to pass the condition property from the child component so that I can use it in the parent component. In addition, is this methodology an anti-pattern? Can I conditionally make the button in the parent disabled based on values from the child component in another way?
Thank you.
try this way
child:
export function ChildOne({setCondition}) {
return (
<div>
<button onClick={() => setCondition(true)}>
hello world
</button>
</div>
);
}
Parent:
import {ChildOne} from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne setCondition={setCondition} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
You should use a state in parent component to control disabled for steps. It can use when you have other pages.
export default function Parent() {
const [condition, setCondition] = useState({});
const changeCondition = (val) => {
setCondition({...condition, [page]: val})
}
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne changeCondition={} />
)}
</div>
<button isDisabled={!condition[page]}>Next</button>
);
}
export function ChildOne({changeCondition}) {
return (
<div>
<button onClick={() => {changeCondition(true)}}>
hello world
</button>
</div>
);
}
You could pass the onClick fucntion as a props param.
Child
export function ChildOne({onClick}) {
return (
<div>
<button onClick={onClick}>
hello world
</button>
</div>
);
}
Parent
import ChildOne, condition from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne onClick={() => setCondition(true)} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
in your Parent component try this :
import ChildOne, condition from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
const handleClick = () => setCondition(true)
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne handleClick={handleClick} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
and in use children :
export function ChildOne({handleClick}) {
return (
<div>
<button onClick={handleClick}>
hello world
</button>
</div>
);
}

Curly braces as function parameter

I want to pass emailID as the second parameter to . Can you help me to understand how to pass additional parameter in Curly braces as a function parameter and how to access it in AccountMenuSidebar.
Sorry for asking this basic question.I am a newbie to Javascript and React.
class Invoices extends Component {
render() {
var emailID="guest#somedomain.com";
const accountLinks = [
{
text: 'Account Information',
url: '/account/user-information',
icon: 'icon-user',
},
{
text: 'Notifications',
url: '/account/notifications',
icon: 'icon-alarm-ringing',
},
];
return (
<section className="ps-my-account ps-page--account">
<div className="container">
<div className="row">
<div className="col-lg-4">
<div className="ps-page__left">
<AccountMenuSidebar data={accountLinks} /> // Want to pass email id as second argument here
</div>
</div>
</div>
</div>
</section>
);
}
}
export default Invoices;
const Accountbar = ({ data }) => (
<aside className="ps-widget--account-dashboard">
<p>{email}</p>
<div className="ps-widget__content">
<ul>
{data.map((link) => (
<li key={link.text} className={link.active ? 'active' : ''}>
<Link href={link.url}>
<a>
<i className={link.icon}></i>
{link.text}
</a>
</Link>
</li>
))}
</ul>
</div>
</aside>
);
export default Accountbar;
<AccountMenuSidebar data={accountLinks} email={emailID} />
and
const Accountbar = (data , emaildID) => (...
or
const Accountbar = (props) => (...
and then you can use props like this...
<ul>
{props.data.map((link) => (
<li key={link.text} className={link.active ? 'active' : ''}>
<Link href={link.url}>
<a>
<i className={link.icon}></i>
{link.text}
</a>
</Link>
</li>
))}
</ul>
When you pass the props from Invoices, you usually acces them like this in AccountMenuSidebar:
<AccountMenuSidebar data={accountLinks} />
const AccountMenuSidebar = (props) => {
return (
<p>{props.data}</p>
)
}
However, using destructuring, which lets you directly unpack variables from an object in JavaScript, you can access the props like this instead:
<AccountMenuSidebar data={accountLinks} />
const AccountMenuSidebar = ({ data }) => {
return (
<p>{data}</p>
)
}
So if you want to send another prop, you can access it the same way, i.e.
<AccountMenuSidebar data={accountLinks} email={email} />
const AccountMenuSidebar = (props) => {
return (
<>
<p>{props.data}</p>
<p>{props.email}</p>
</>
)
}
or using destructuring:
<AccountMenuSidebar data={accountLinks} email={email} />
const AccountMenuSidebar = ({ data, email }) => {
return (
<>
<p>{data}</p>
<p>{email}</p>
</>
)
}

How do I pass my click handler from parent to children correctly?

I'm stuck trying to refactor my scoreboard component because I can't pass click handlers from parent to child correctly. What am I doing wrong?
This is my component structure
import React, { useState } from "react";
import "./App.css";
function ScoreBoard(props) {
return (
<section className="scoreboard">
<div className="topRow">
<div className="home">
<h2 className="home__name">{props.data.home.name}</h2>
<div className="home__score">{props.data.home.score}</div>
</div>
<div className="timer">00:03</div>
<div className="away">
<h2 className="away__name">{props.data.away.name}</h2>
<div className="away__score">{props.data.away.score}</div>
</div>
</div>
<BottomRow />
</section>
);
}
function TDButton(props) {
return (
<button className={props.side + "Buttons__touchdown"}>
{props.side.toUpperCase() + " Touchdown"}
</button>
);
}
function FGButton(props) {
/* similar to TDButton */
}
function Buttons(props) {
let scoreCounter = props.scoreCounter;
return (
<section className="buttons">
<div className="homeButtons">
<TDButton side="home" onClick={scoreCounter("Lions", 7)} />
<TDButton side="away" onClick={scoreCounter("Tigers", 7)} />
</div>
<div className="awayButtons">
<FGButton side="home" onClick={scoreCounter("Lions", 3)} />
<FGButton side="away" onClick={scoreCounter("Tigers", 3)} />
</div>
</section>
);
}
function App() {
const data = {
home: { name: "Lions", score: 32 },
away: { name: "Tigers", score: 32 }
};
const [homeScore, sethomeScore] = useState(data.home.score);
const [awayScore, setawayScore] = useState(data.away.score);
const scoreCounter = (team, amount) => {
if (team === data.home.name) {
console.log("in");
sethomeScore(homeScore + amount);
} else {
console.log("out");
setawayScore(awayScore + amount);
}
};
return (
<div className="container">
<ScoreBoard data={data} />
<Buttons data={data} scoreCounter={() => scoreCounter} />
</div>
);
}
The initial component all lived in App so I am trying to break it into smaller components. I can't seem to get the click handler to work though. What am I doing wrong? Maybe my component breakdown could be improved? Thanks!
You adding props to TButton but you don't use it inside. Use something like this:
function TDButton(props) {
return (
<button className={props.side + "Buttons__touchdown"} onClick={props.onClick}>
{props.side.toUpperCase() + " Touchdown"}
</button>
);
}
so it's like:
<TDButton side="home" onClick={scoreCounter("Lions", 7)} />
<button className={props.side + "Buttons__touchdown"} onClick={scoreCounter("Lions", 7)}>
{props.side.toUpperCase() + " Touchdown"}
</button>
because you're passing the props to event from parent.
but this will only work if scoreCounter("Lions", 7) return a function, if it's regular function that do action you need:
<TDButton side="home" onClick={() => scoreCounter("Lions", 7)} />
so props is a function not the value that function return.
Also this maybe not what you want:
<Buttons data={data} scoreCounter={() => scoreCounter} />
scoreCounter will be function that return value of function (that answer previous consern but you really want normal function, because above function don't have params and you use 'Lion' that will be ignored):
<Buttons data={data} scoreCounter={scoreCounter} />
SO you've to call the function in your JSX
<Buttons data={data} scoreCounter={() => scoreCounter()} />
You can pass value into the function if required.

lodash map get index key got unexpected token

import { map } from 'lodash';
render(){
return(
{map(new_applicants, (obj,index) =>
<div key={index}>{index}</div>
)}
)
}
What's wrong with this code? obj is the single array of object been iterate and index is the key. I'm using lodash. The error look like this in console.
{map(new_applicants, (obj,index) =>
| ^
The problem is that the {...} syntax is being taken for an object initializer; you're doing this outside of JSX. That syntax is only valid within a JSX section, e.g.
<div>{map(...)}</div>
Also, render has to return a component (or null), it can't return an array. So perhaps:
return(
<div>
{map(new_applicants, (obj,index) =>
<div key={index}>{index}</div>
)}
</div>
)
Example:
const map = _.map;
class Foo extends React.Component {
render() {
const new_applicants = [1, 2, 3];
return(
<div>
{map(new_applicants, (obj,index) =>
<div key={index}>{index}</div>
)}
</div>
)
}
}
ReactDOM.render(
<Foo />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
Write it like this, {} it required when you are running the js code inside html element:
render(){
return(
<div>
{
map(new_applicants, (obj,index) =>
<div key={index}>{index}</div>
)
}
</div>
)
}

Categories