Components in React - reactjs

I have the following code from the React documentation. Question:
Why do i have to use the formatDate function? Lets say i just want to use the raw date output from the new Date () function, why cant i just use it as props.date? does not seem to work.
function formatDate(date) {
return date.toLocaleDateString();
}
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name} />
);
}
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
const comment = {
date: new Date(),
text: 'I hope you enjoy learning React!',
author: {
name: 'Hello Kitty',
avatarUrl: 'http://placekitten.com/g/64/64'
}
};
ReactDOM.render(
<Comment
date={comment.date}
text={comment.text}
author={comment.author} />,
document.getElementById('root')
);

Related

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

How can I pass parameters from one function to another

I am new to React and trying to figure something out. I have written the code below that should pass the values from one function to another and at the end accept parameters from an object to populate them. However, I can only see the text property of the nameInput object as an output. Please let me know what am I doing wrong?
import React, { Component } from 'react';
function FirstName(props){
return <h1>Hi, {props.fname.name}</h1>
}
function LastName(props) {
return (
<div className="UserInfo">
<FirstName fname={props.lname} />
<h2>{props.lname.name}</h2>
</div>
);
}
function FullName(props){
return (
<div className="container">
<LastName lname={props.fulname} />
<div>{props.text}</div>
</div>
);
}
const nameInput = {
name: {
firstname: 'Savi',
lastname: 'Dulai',
},
text: 'got it to work?'
};
class ComponentPractise extends Component {
state = { }
render() {
return (
<div className="row">
<FullName
text={nameInput.text}
fulname={nameInput.name}
/>
</div>
);
}
}
export default ComponentPractise;
I would like the output to say "Hi, Savi Dulai got it to work?".
Any help would be much appreciated.
Seem like you was lost the context. Function FullName is okay, but LastName and FirstName referenced to wrong props property.
import React, { Component } from 'react';
function FirstName(props){
return <h1>Hi, {props.fname.firstname}</h1>
}
function LastName(props) {
return (
<div className="UserInfo">
<FirstName fname={props.lname} />
<h2>{props.lname.lastname}</h2>
</div>
);
}
function FullName(props){
return (
<div className="container">
<LastName lname={props.fulname} />
<div>{props.text}</div>
</div>
);
}
const nameInput = {
name: {
firstname: 'Savi',
lastname: 'Dulai',
},
text: 'got it to work?'
};
class ComponentPractise extends Component {
state = { }
render() {
return (
<div className="row">
<FullName
text={nameInput.text}
fulname={nameInput.name}
/>
</div>
);
}
}
export default ComponentPractise;
It should works, but I recommend you to change review your function and variable naming to make it more readable.
Hope that helps!
You're close to the answer, it is just the matter of accessing the props properly. Your fulname prop is an object itself:
name: {
firstname: 'Savi',
lastname: 'Dulai',
}
Access it as props.fulname.firstname and props.fulname.lastname.
<!DOCTYPE html>
<html lang="en">
<title>myPage</title>
<script src= "https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src= "https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<script src= "https://unpkg.com/babel-standalone#6.15.0/babel.min.js"></script>
<body>
<div id="root"></div>
<script type="text/babel">
function FirstName(props){
return <h1>Hi, {props.fname}</h1>
}
function LastName(props) {
return (
<div className="UserInfo">
<h2>{props.lname}</h2>
</div>
);
}
function FullName(props){
return (
<div className="container">
<FirstName fname={props.fulname.firstname} />
<LastName lname={props.fulname.lastname} />
<div>{props.text}</div>
</div>
);
}
const nameInput = {
name: {
firstname: 'Savi',
lastname: 'Dulai',
},
text: 'got it to work?'
};
class ComponentPractise extends React.Component {
state = { }
render() {
return (
<div className="row">
<FullName
text={nameInput.text}
fulname={nameInput.name}
/>
</div>
);
}
}
ReactDOM.render(<ComponentPractise />, document.getElementById('root'));
</script>
</body>
</html>
Refactoring/suggestion
I'd suggest refactoring the code to this, you don't even need the two extra components if you do is this way!
<!DOCTYPE html>
<html lang="en">
<title>myPage</title>
<script src= "https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src= "https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<script src= "https://unpkg.com/babel-standalone#6.15.0/babel.min.js"></script>
<body>
<div id="root"></div>
<script type="text/babel">
function FullName(props){
return (
<div className="container">
<h2>Hi {props.fullname.firstname} {props.fullname.lastname}!</h2>
<div>{props.text}</div>
</div>
);
}
const nameInput = {
name: {
firstname: 'Savi',
lastname: 'Dulai',
},
text: 'got it to work?'
};
class ComponentPractise extends React.Component {
state = { }
render() {
return (
<div className="row">
<FullName
text={nameInput.text}
fullname={nameInput.name}
/>
</div>
);
}
}
ReactDOM.render(<ComponentPractise />, document.getElementById('root'));
</script>
</body>
</html>
You've made this example highly complex, but i guess you're just playing around. So am i:) Best thing to do is stick to one name for stuff you want to pass along. Also, you can console.log(props) above the return within functions to check what is being passed along.
import React, { Component } from "react";
function FirstName(props) {
return <h1>Hi, {props.firstname}</h1>;
}
function LastName(props) {
return (
<div className="UserInfo">
<FirstName firstname={props.nameObject.firstname} />
<h2>{props.text}</h2>
</div>
);
}
function FullName(props) {
return (
<div className="container">
<LastName nameObject={props.nameObject} />
<div>{props.text}</div>
</div>
);
}
const nameInput = {
name: {
firstname: "Savi",
lastname: "Dulai"
},
text: "got it to work?"
};
class ComponentPractise extends Component {
state = {};
render() {
return (
<div className="row">
<FullName text={nameInput.text} nameObject={nameInput.name} />
</div>
);
}
}
export default ComponentPractise;
Also, you can use IE6 syntax to clean things up a lot, since react parses it back into code all browsers can handle (IE6 JS isn't supported yet). Makes much nicer work. Also got rid of the functional component at the end.
import React from "react";
const FirstName = props => {
return <h1>Hi, {props.firstname}</h1>;
};
const LastName = props => (
<div className="UserInfo">
<FirstName firstname={props.nameObject.firstname} />
<h2>{props.text}</h2>
</div>
);
const FullName = props => (
<div className="container">
<LastName nameObject={props.nameObject} />
<div>{props.text}</div>
</div>
);
const nameInput = {
name: {
firstname: "Savi",
lastname: "Dulai"
},
text: "got it to work?"
};
export default () => (
<div className="row">
<FullName text={nameInput.text} nameObject={nameInput.name} />
</div>
);

TypeError: author is undefined

creating react AuthorQuiz app
I have tow main file
1- AuthorQuiz.js
2- index.js
I have a problem with Turn component
AuthorQuiz.js
enter code here
function Turn({ author, books }) {
return (
<div className="row turn" style={{ backgroundColor: 'white' }}>
<div className="col-4 offset-1">
<img src={author.imageUrl} className="authorImage" alt="Author" />
</div>
<div className="col-6">
{books.map((title) => <p>{title}</p>)}
</div>
</div>);
}
function AuthorQuiz(turnData) {
return (
<div className="container-fluid">
<Hero/>
<Turn {...turnData}/>
<Continue/>
<Footer/>
</div>
);
}
index.js
enter code here
const authors = [
{
name: 'mark Twin',
imageUrl: 'images/authors/mark.jpg',
imageSource: 'google wiki',
books: ['The Advance of Finn']
}
];
const state={
turnData:{
author:authors[0],
books:authors[0].books
}
}
ReactDOM.render(<AuthorQuiz {...state}/>,
document.getElementById('root'));
but when I run my code I get an error
TypeError: author is undefined
Turn
C:/Users/HP/Desktop/React pro/authorquiz/src/AuthorQuiz.js:18
You should use {state.turnData} instead of {...state}.
Because the result of {...state} is like this: {turnData: {…}}
So { author, books } can't work correctly.
const state={
turnData:{
author:authors[0],
books:authors[0].books
}
}
<AuthorQuiz {...state}/>
Spreading the state over the AuthorQuiz is equivalent to:
<AuthorQuiz turnData={state.turnData}/>
But in the AuthorQuiz component:
function AuthorQuiz(turnData) {
return (
<div className="container-fluid">
<Hero/>
<Turn {...turnData}/>
<Continue/>
<Footer/>
</div>
);
}
function AuthorQuiz(turnData) {
...
<Turn {...turnData}/>
...
is equivalent to
function AuthorQuiz(props) {
...
<Turn turnData={
props.turnData
}}/>
...
So you need to add brackets to spread turnData instead of props:
function AuthorQuiz({ turnData }) {
...
<Turn { ...turnData}}/>
...
The AuthorQuiz component is getting the turnData variable as a prop. You should use the spread operator on the props.turnData variable instead.
function AuthorQuiz(props) {
return (
<div className="container-fluid">
<Hero/>
<Turn {...props.turnData}/>
<Continue/>
<Footer/>
</div>
);
}
The props in the AuthorQuiz component look like this:
{
turnData: {
author: { .. },
books: [ .. ]
}
}
Your Turn component wants the author and books props, so you could do this in your AuthorQuiz component.
function AuthorQuiz(props) {
return (
<div className="container-fluid">
<Hero/>
<Turn {...props.turnData} />
{/* or */}
<Turn author={props.turnData.author} books={props.turnData.books} />
<Continue/>
<Footer/>
</div>
);
}
You could also destructure the turnData prop in your component function directly. This makes it clear which props are being drilled down the Turn component without switching to multiple files.
function AuthorQuiz({ turnData: { author, books } }) {
return (
<div className="container-fluid">
<Hero/>
<Turn author={author} books={books} />
<Continue/>
<Footer/>
</div>
);
}

TypeError: books.map is not a function

I'm creating a react app.
I have 2 files
AuthorQuiz.js
index.js
And I have 2 components
AuthorQuiz
Turn
Turn component is embedded in the AuthorQuiz component and the AuthorQuiz is the main component.
And data is coming from index.js as a spread operator
AuthorQuiz.js
function AuthorQuiz({ turnData }) {
return (
<div className="container-fluid">
<Turn {...turnData} />
</div>
);
}
function Turn(author, books) {
return (
<div className="row turn" style={{ backgroundColor: 'white' }}>
<div className="col-4 offset-1">
<img src={author.imageUrl} className="authorImage" alt="Author" />
</div>
<div className="col-6">
{books.map(title => (
<p>{title}</p>
))}
</div>
</div>
);
}
index.js
const authors = [
{
name: 'Mark Twain',
imageUrl: 'images/authors/marktwain.jpg',
imageSource: 'Wikimedia Commons',
books: ['The Adventures of Hucklebery Finn']
}
];
const state = {
turnData: {
author: authors[0],
books: authors[0].books
}
};
ReactDOM.render(<AuthorQuiz {...state} />, document.getElementById('root'));
but when I run my code I get an error
TypeError: books.map is not a function
You haven't destructured the props in Turn component, which leads to it being treated incorrectly by the component
function Turn({ author, books }) { // Destructure props here
return (
<div className="row turn" style={{ backgroundColor: 'white' }}>
<div className="col-4 offset-1">
<img src={author.imageUrl} className="authorImage" alt="Author" />
</div>
<div className="col-6">
{books.map(title => (
<p>{title}</p>
))}
</div>
</div>
);
}

Push component to array of components - ReactJS

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 -:) )

Resources