Push component to array of components - ReactJS - arrays

I am a bit lost in React arrays. What I want to do is to have array of components (articles) and in that array I want to have title and content.
What I want to do with that array is add, remove and display it on my page.
So what am I doing wrong? Also what is this action exactly called?
Code was from ReactJS demos and modified a little by me.
var ReactDOM = require('react-dom');
var React = require('react');
// Articles page
const Articles = React.createClass({
getInitialState() {
return {article: [
{'title': 'hello', 'content': 'hello hello'},
{'title': 'hello1', 'content': 'hello hello1'},
{'title': 'hello2', 'content': 'hello hello2'},
{'title': 'hello3', 'content': 'hello hello3'}
]};
},
onAdd() {
const newArticle =
this.state.article.concat([window.prompt('Enter article')]);
this.setState({article: newArticle});
},
onRemove(i) {
const newArticle = this.state.article;
newArticle.splice(i, 1);
this.setState({article: newArticle});
},
render() {
const article = this.state.article.map((article, i) => {
return (
<div key={article} onClick={this.onRemove.bind(this, i)}>
{article}
</div>
);
});
return (
<div className="container">
<div className="row">
<div className="col-md-12 cBusiness">
<p>Articles Page</p>
<button onClick={this.onAdd}>Add Article</button>
<br />
<br />
{title}
<br />
<br />
{content}
</div>
</div>
</div>
);
},
});
module.exports = Articles;

In your render method there are no variables title and content, however there is variable article where you are creating list with articles, you should remove variable title and content from render and use article. I've refactored your code, and now it looks like this
const Articles = React.createClass({
getInitialState() {
return {
articles: [
{'title': 'hello', 'content': 'hello hello'},
{'title': 'hello1', 'content': 'hello hello1'},
{'title': 'hello2', 'content': 'hello hello2'},
{'title': 'hello3', 'content': 'hello hello3'}
]
};
},
onAdd() {
const title = window.prompt('Enter article title');
const content = window.prompt('Enter article content');
this.setState({
articles: this.state.articles.concat({ title, content })
});
},
onRemove(index) {
this.setState({
articles: this.state.articles.filter((e, i) => i !== index)
});
},
render() {
const articles = this.state.articles.map((article, i) => {
return (
<div key={i}>
<h2>
{ article.title }
<span
className="link"
onClick={ this.onRemove.bind(this, i) }
>
X
</span>
</h2>
<p>{ article.content }</p>
</div>
);
});
return (
<div className="container">
<div className="row">
<div className="col-md-12 cBusiness">
<p>Articles Page</p>
<div>{ articles }</div>
<button onClick={this.onAdd}>Add Article</button>
</div>
</div>
</div>
);
},
});
Example

In your render you are creating the rows of your list and you have assigned it to the const called 'article'. However your render is:
return (
<div className="container">
<div className="row">
<div className="col-md-12 cBusiness">
<p>Articles Page</p>
<button onClick={this.onAdd}>Add Article</button>
<br />
<br />
{title}
<br />
<br />
{content}
</div>
</div>
</div>
);
There is no where you are rendering the 'article' created. You could replace {content} with {article}? As I have seen no declaration of content anywhere.
Also, just an aside, you have named so many variables as 'article'. It's a bit hard to follow, might help pluralizing in some cases (but this does not relate to the question asked -:) )

Related

Mapping through a object that is the only object in an array of list (nested object)

Im having a hard time mapping through an object in an array. So i have this code and im able to display the array but the comments (object) is not showing. that is im not able to access my nested object. I have scouted the internet and all the solutions there havent helped me. Any ideas or push to the right direction is greatly appreciated
function SingleTweet() {
const [count, setCount] = useState(0)
const [single, setSingle] = useState({})
const { id } = useParams()
useEffect(() => { axios.get(`https://localhost:44368/api/users/${id}`).then((res) => {
console.log(res.data, "list of heroes ");
setSingle(res.data);
});
}, []);
const [coms, setCom] = useState({})
useEffect(() => { axios.get(`https://localhost:44368/api/comments/${id}`).then((res) => {
console.log(res.data, "list of Comments ");
setCom(res.data);
});
}, []);
return (
<div className="app">
<LeftSidebar />
<div>
<div>
<h2>Home</h2>
</div>
<div className="post-1">
<div>
<Avatar src={single.profilePicture} />
</div>
<div>
<div>
<div>
<h3>
{single.name}
<div>
#{single.userName}
</div>
</h3>
</div>
<div className="post_headerDesription-1">
<p>{single.post}</p>
</div>
</div>
<img src={single.image} alt="" />
<div className="p_footer">
<Tweet />
<RepeatIcon />
<FavoriteBorderIcon />
<PublishOutlinedIcon />
</div>
</div>
</div>
{/* i want to display the comments here that belong to the post */}
</div>
<RightSidebar />
</div>
)
}
this is my console log of the api call
comments: (2) [{…}, {…}]
id: 1
image: "https://media2.giphy.com/media/Q2tS8xloz0cg0/giphy.gif"
name: "Robin"
post: "Batman said i couldnt go out tonight :("
profilePicture: "https://variety.com/wp-content/uploads/2021/08/Robin.jpg?w=681&h=383&crop=1"
userName: "robin"

How do I conditionally render pages with a dropdown menu in React?

I'm fairly new to React and I'm working on a website for a friend that uses a lot of react features. One thing this website needs is a navbar where every item in the navbar has a dropdown selection of additional nav items. I'm able to both render pages conditionally as independent nav items and create the hover dropdown on each nav item, but my issue comes into merging them together. I've tried a few things such as mapping through props twice, creating a large object where the nav item is a name and the dropdown items are subnames, but neither of those worked.
Here is the code I'm using:
function Nav(props) {
const [navItemList, setNavItemList] = useState([
{name: 'About', dropdownItem1: 'About Me', dropdownItem2: 'About Tampa Bay', id: 1},
]);
const { pages = [], setCurrentPage, currentPage } = props;
return (
<header className="flex-row">
<h1 class="name-tag">
<img src={"../../assets/Logo1.png"} />
</h1>
<nav>
<NavItems items={navItemList} />
<ul className="flex-row nav-list">
{pages.map(navItem => (
<li className={`li-spacing text-format ${currentPage.name === navItem.name && 'navActive'}`} key={navItem.id}>
<span onClick={() => { setCurrentPage(navItem) }}>{navItem.name}</span>
</li>
))}
</ul>
</nav>
</header>
)
}
function App() {
const [pages] = useState([
{
id: 1,
name: 'Home'
},
{
id: 2,
name: 'About Me'
},
{
id: 3,
name: 'About Tampa Bay'
},
])
const [currentPage, setCurrentPage] = useState(pages[0])
return (
<div>
<Nav
pages={pages}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
></Nav>
<main>
<Pages currentPage={currentPage}></Pages>
</main>
</div>
);
}
function NavItems(props) {
const items = props.items
return (
<ul className=" flex-row nav-list">
{/* map through the props so each navitem receives unique information */}
{items.map((navItem) => (
<div className="dropdown" key={navItem.id}>
<li className="nav-list-item">{ navItem.name }</li>
<div className="dropdown-item">
<p>{ navItem.dropdownItem1 }</p>
<p>{ navItem.dropdownItem2 }</p>
</div>
</div>
)) }
</ul>
)
}
export default NavItems;
Something like this maybe? This might need to be adjusted a bit to fit your styling needs.
const pages = {
home: {
name: 'Home',
subPages: {},
},
about: {
name: 'About',
subPages: {
aboutMe: {
name: 'About Me',
},
aboutTampaBay: {
name: 'About Tampa Bay',
},
},
},
}
function App() {
const [currentPageKey, setCurrentPageKey] = useState('home')
return (
<div>
<Nav pages={pages} currentPage={currentPage} setCurrentPageKey={setCurrentPageKey} />
<main>
<Pages currentPage={pages[currentPageKey]} />
</main>
</div>
)
}
function Nav(props) {
const { setCurrentPageKey, currentPage, pages } = props
return (
<header className="flex-row">
<h1 class="name-tag">
<img src={'../../assets/Logo1.png'} />
</h1>
<nav>
<ul className="flex-row nav-list">
{Object.entries(pages).map(([key, { name, subPages }]) => (
<li className={`li-spacing text-format ${currentPage.name === name && 'navActive'}`} key={key}>
<NavItems setCurrentPageKey={setCurrentPageKey} title={name} items={subPages} />
<button onClick={() => setCurrentPageKey(key)}>{name}</button>
</li>
))}
</ul>
</nav>
</header>
)
}
export default function NavItems(props) {
const { setCurrentPageKey, items, title } = props
return (
<ul className="flex-row nav-list">
<div className="dropdown">
<li className="nav-list-item">{title}</li>
<div className="dropdown-item">
{/* map through the props so each navitem receives unique information */}
{Object.entries(items).map(([key, { name }]) => (
<button onClick={() => setCurrentPageKey(key)} key={key}>
{name}
</button>
))}
</div>
</div>
</ul>
)
}

Loop through array in React and create elements (not list items) from it

I'm learning React and have done a fair bit of research on this. I've quickly discovered that the map() function is what I think I should be using for looping through an array.
But, my problem is all the examples in the React documentation and in the SO questions I've viewed use <ul> and <li> HTML elements to handle the output.
I'm not sure that my use case is "correct" as far as React structure is concerned, but, I want to output a <div> with some child elements each time I loop through.
Here is my static code so far:
const Comment = () => {
return (
<div className="commenter">
<div className="commenter-inner">
<div className="commenter-left">
<img src={chachi}/>
<p className="commenter-name">Scott Baio</p>
</div>
<div className="commenter-comment">
<p>Ehhhh!! Joanie loves Chachi!!!</p>
</div>
</div>
</div>
)
}
This works, but now if I have additional comments I want to be able to serve up the same block of code again but with the new commenters name, image, comment content etc.
So I've now made an array to house my multiple commenters, and things aren't really working anymore.
import React, { Component } from 'react'
import fonzie from "./img/the-fonz.jpg";
import chachi from "./img/chachi.jpg";
const Comments = [
{
id: 1,
name: 'Hello World',
photo: fonzie,
comment: 'Welcome to learning React!'
},
{
id: 2,
name: 'Hello World',
photo: chachi,
comment: 'Welcome to learning React!'
}
];
const commentEngine = props.Comments.map((comment) =>
<div className="commenter" key={comment.id}>
<div className="commenter-inner">
<div className="commenter-left">
<img src={comment.photo}/>
<p className="commenter-name">{comment.name}</p>
</div>
<div className="commenter-comment">
<p>{comment.comment}</p>
</div>
</div>
</div>
);
class Comments extends Component {
render() {
return (
<div className="comments-section col-md-10 offset-md-1 col-sm-12">
<h4>Comments</h4>
<commentEngine />
</div>
);
}
}
export default Comments
At this point I'm unsure how to verify if my loop is working in the first place and how to get the output properly displaying in my app.
Any help is greatly appreciated, as is insight into whether or not this is well structured or should be separate components.
Thanks!
It sounds like you want to re-use the Comment component with data passed by Comments. In React, this is done via props.
So, you'll want to pass the images's src, the name, and the description:
const comments = [
{
id: 1,
name: "Hello World",
photo: fonzie,
comment: "Welcome to learning React!",
},
{
id: 2,
name: "Hello World",
photo: chachi,
comment: "Welcome to learning React!",
},
];
class Comments extends Component {
render() {
return (
<div className="comments-section col-md-10 offset-md-1 col-sm-12">
<h4>Comments</h4>
{comments.map((comment) => {
return (
<Comment
key={comment.id} // https://reactjs.org/docs/lists-and-keys.html
name={comment.name}
imgSrc={comment.photo}
comment={comment.comment}
/>
);
})}
</div>
);
}
}
Notice that I've renamed the constant array Comments to comments so that the name doesn't clash with the Comments component.
Then in the Comment component, you can access these props via the argument passed to the function component:
const Comment = (props) => {
return (
<div className="commenter">
<div className="commenter-inner">
<div className="commenter-left">
<img src={props.imgSrc} />
<p className="commenter-name">{props.name}</p>
</div>
<div className="commenter-comment">
<p>{props.comment}</p>
</div>
</div>
</div>
);
};
Additionally, we can make the code a bit less verbose by leveraging object destructuring:
class Comments extends Component {
render() {
return (
<div className="comments-section col-md-10 offset-md-1 col-sm-12">
<h4>Comments</h4>
{comments.map(({ id, name, photo, comment }) => {
return (
<Comment key={id} name={name} imgSrc={photo} comment={comment} />
);
})}
</div>
);
}
}
// ...
const Comment = ({ imgSrc, name, comment }) => {
return (
<div className="commenter">
<div className="commenter-inner">
<div className="commenter-left">
<img src={imgSrc} />
<p className="commenter-name">{name}</p>
</div>
<div className="commenter-comment">
<p>{comment}</p>
</div>
</div>
</div>
);
};
const commentEngine = (comments) => {
return comments.map((comment)=>{
return (
<div className="commenter" key={comment.id}>
<div className="commenter-inner">
<div className="commenter-left">
<img src={comment.photo}/>
<p className="commenter-name">{comment.name}</p>
</div>
<div className="commenter-comment">
<p>{comment.comment}</p>
</div>
</div>
</div>
)})
class Comments extends Component {
render() {
return (
<div className="comments-section col-md-10 offset-md-1 col-sm-12">
<h4>Comments</h4>
{commentEngine(props.Comment)}
</div>
);
}
}
Now when you render Comments you need to pass the Comment props.
<Comments Comment={Comments}/>
USAGECASE
import React, { Component } from 'react'
import fonzie from "./img/the-fonz.jpg";
import chachi from "./img/chachi.jpg";
const Comments = [
{
id: 1,
name: 'Hello World',
photo: fonzie,
comment: 'Welcome to learning React!'
},
{
id: 2,
name: 'Hello World',
photo: chachi,
comment: 'Welcome to learning React!'
}
];
const Comment = props =>
const {comment} = props;
<div className="commenter" key={comment.id}>
<div className="commenter-inner">
<div className="commenter-left">
<img src={comment.photo}/>
<p className="commenter-name">{comment.name}</p>
</div>
<div className="commenter-comment">
<p>{comment.comment}</p>
</div>
</div>
</div>
);
class Comments extends Component {
render() {
return (
<div className="comments-section col-md-10 offset-md-1 col-sm-12">
<h4>Comments</h4>
{Comments.map((comment,index) => <Comment key={'[CUSTOM_KEY]'} props={comment}> )}
</div>
);
}
}
export default Comments
ANSWER
First of all, You can use index parameter in Array.map
Secondly, if you want to use list component you can make Single Component like <Comment comment={comment}> and you can use it with Array.map
And It is very good to study How to make functional component

Render N times component based on the data in object

I am new at React. Will be glad if someone can help:
I have parent (Dashboard) which contains all data. This data is passed to the children component (OnBoardingCard).
How can I render n times the OnBoardingCard component based on the data in the object at Dashboard without using the [num](in this case 3 times - 3x OnBoarding Cards;)?
Thank you!!
Parent- Dashboard
const cardData = [
{
svg: icon1,
title: 'Add',
content: 'add more'},
{
svg: icon2,
title: 'remove',
content: 'remove'
},
{
svg: icon3,
title: 'move',
content: 'move down'
}];
class Dashboard extends Component {
render() {
return (
<Section>
<OnboardingCard listData={cardData}/>
</Section>
);
} }
Children- OnBoardingCard
import Dashboard from "../../../../screens/Dashboard/index.js";
class OnboardingCard extends Component {
render() {
return (
<div className={styles.cardHolder}>
<div className={styles.fullCard}>
<div className={styles.onboardingCard}>
<div className={styles.iconBackground}>
<img src={this.props.listData[0].svg} />
</div>
<div className={styles.title}>{this.props.listData[0].title}</div>
</div>
<p className={styles.cardDescription}>
{this.props.listData[0].content}
</p>
</div>
</div>
); }}
When you are using a map inside render assign a unique key to its child component.
render(){
return(
{this.props.listData.map((item, i) =>
<div className={styles.cardHolder} key={i}>
<div className={styles.fullCard}>
<div className={styles.onboardingCard}>
<div className={styles.iconBackground}>
<img src={this.props.listData[0].svg} />
</div>
<div className={styles.title}>{this.props.listData[0].title}</div>
</div>
<p className={styles.cardDescription}>
{this.props.listData[0].content}
</p>
</div>
</div>
)}
);
}
You can use map function,
like this,
{this.props.listData.map((item)=>
<div className={styles.cardHolder}>
<div className={styles.fullCard}>
<div className={styles.onboardingCard}>
<div className={styles.iconBackground}>
<img src={item.svg} />
</div>
<div className={styles.title}>{item.title}</div>
</div>
<p className={styles.cardDescription}>
{item.content}
</p>
</div>
</div>)}
<Section>
<div className={styles.cardRow}>
{cardData.map((card, i) => (
<OnboardingCard {...card} key={i} />
))}
</div>
</Section>
This is what I meant (and wanted to do). So this solves my question. Thanks everyone!!

How to load firestore subcollection to redux store?

I'm having trouble loading firestore subcollection in redux store. I'm trying to load respective subcollection inside the child component.
The component structure looks like this:
Dashboard
--Project list
--project summary
--comments
I have loaded collection named Projects using firestoreConnect inside Dashboard component passing data to Project list mapping data to Project Summary. comments is a child component of project summary
My firestore collection structure looks like this:
Projects
--project1
--comments
Dashboard:
class Dashboard extends Component {
render() {
const { projects, auth, notifications } = this.props
if (!auth.uid) return <Redirect to='/signin' />
return(
<div className="dashboard container">
<div className="row">
<div className="col s12 m8 offset-m2 l8">
<ProjectList projects={projects} />
</div>
<div className="col s12 offset-m1 hide-on-med-and-down l4">
<Notification notifications={notifications} />
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
// console.log("ref: ",state)
return {
projects: state.firestore.ordered.projects,
initials: state.firebase.profile.initials,
auth: state.firebase.auth,
notifications: state.firestore.ordered.notifications
}
}
export default compose (
connect(mapStateToProps),
firestoreConnect([
{ collection: 'projects', orderBy: ['createdAt', 'desc'] },
{ collection: 'notifications', limit: 3, orderBy: ['time', 'desc'] }
])
)(Dashboard)
Project list Component:
const ProjectList = ({ projects }) => {
return(
<div className="project-list section">
{projects && projects.map((project) => {
return(
<div key={project.id}>
<ProjectSummary project={project}/>
</div>
)
})}
</div>
)
}
export default ProjectList
Project summry:
const ProjectSummary = ({ project }) => {
return(
<div className="card z-depth-0 project-summary show-up post">
<div className="name">
<div className="">
<span className="btn btn-floating z-depth-0 black user-indicator">
{project.authorFirstName && project.authorFirstName[0]}{project.authorSecondName && project.authorSecondName[0]}
</span>
<span> {project.authorFirstName {project.authorSecondName} </span>
<span className="right options"> <i className="material-icons">more_vert</i> </span>
</div>
</div>
<div className="card-image">
<img src={project.imageUrl} alt="art" />
<PostCredits />
</div>
<div className="card-reveal">
<Comments id={project.id} />
</div>
<div className="card-content">
<Link to={'/projectdetails/' + project.id} className="black-text">
<p className="post-title"> {project.title} </p>
</Link>
<p className="grey-text lighten-3 load-comments activator"> Load comments </p>
<p className="grey-text date-format">
{project.createdAt && project.createdAt
.toDate()
.toLocaleDateString('indian', {
year: "numeric", month: "short", day: "numeric"
})}
</p>
</div>
<div className="add-comment">
<AddComment projectId={project.id} />
</div>
</div>
)
}
export default ProjectSummary
Comment:
const Comment = (props) => {
return(
<div>
<div className="loaded-comments">
<span className="card-title grey-text text-darken-4"> Comments <i className="material-icons right">close</i> </span>
<p> <span className="commentor"> Joe </span> </p>
</div>
</div>
)
}
const mapStateToProps = (state, ownProps) => {
console.log("state: ", state)
return {
}
}
export default compose (
connect(mapStateToProps),
firestoreConnect(props => {
return [
{ collection: 'projects',
doc: props.id,
subcollections: [
{
collection: 'comments'
}
]
}
]
})
)(Comment)
Expected Result:
*Want to load Project collection's subcollection named Comments
Actual Result:
*No error occurs but still no data is loaded.
Thanks in advance!
There is a possibility that you just don't see that sub-collection.
If you look under in your browser console, navigate to firebase > ordered > prjoects > comments. you should be able to see an array of your comment
Example:
|-firestore
|--ordered:
|---projects: Array(1)
|----0:
|-----id: *uid*
|------comments: Array(2)
|-------0: {*comment details*}
|-------1: {*comment details*}
|------length: 2
|-----__proto__: Array(0)
After you can map your data based on your key.
const mapStateToProps = (state) => {
return {
projects: state.firestore.ordered.projects.comments,
}
}
Once connected you will be able to use your data.

Resources