How {props.children} is different from normal objects? - javascript

I am learning React with the help of a crash course. I was learning about nested components and props where I have some confusion. I was making a small comment section.Like thisHere I have used two components the CommentDetail.js where user's name,image and comment will display and ApprovalCard.js which is for approving the comment made by user(Approve/Reject).CommentDetail here is child component of ApprovalCard here.The instructor told that when we nest the components the parent component receive object in props.And this props contains a property children which we can render.What I am not understanding is when I console.log(props) in ApprovalCard.js children itself is an object inside props.As object can not render in JSX than how {props.children} is working here ?Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import faker from 'faker';
import CommentDetail from './CommentDetail';
import ApprovalCard from './ApprovalCard';
const App = () => {
return (
<div className='ui comments'>
<ApprovalCard>
<CommentDetail author='Maximillian' timesAgo = 'Today at 4:00PM' comment = 'Nice blog post!'
image = {faker.image.avatar()}/>
</ApprovalCard>
<ApprovalCard>
<CommentDetail author='Kishan' timesAgo = 'Today at 1:00AM' comment = 'Touched every important point.'
image = {faker.image.avatar()}/>
</ApprovalCard>
<ApprovalCard>
<CommentDetail author='Punit' timesAgo = 'Today at 6:32PM' comment = 'Far better than any other article I have come across'
image = {faker.image.avatar()}/>
</ApprovalCard>
</div>
);
};
ReactDOM.render(<App/>,document.querySelector('#root'));
CommentDetail.js
import React from 'react';
const CommentDetail = (props)=>
{
console.log(props);
return (
<div className="comment">
<a className='avatar'>
<img alt="avatar" src={props.image}/>
</a>
<div className='content'>
<a className='author'>{props.author}</a>
<div className='metadata'>
<span className='date'>{props.timesAgo}</span>
</div>
<div className='text'>
{props.comment}
</div>
</div>
</div>
);
};
export default CommentDetail;
ApprovalCard.js
import React from 'react';
const ApprovalCard = (props) => {
console.log(props.children);
return (
<div className = "ui card">
<div className="content">{props.children}</div>
<div className="extra content">
<div className="ui two buttons">
<div className="ui basic green button">Approve</div>
<div className="ui basic red button">Reject</div>
</div>
</div>
</div>
);
};
export default ApprovalCard;

Your code is containing the following <ApprovalCard> component which has one component inside which is the <CommentDetail> child:
<ApprovalCard>
<CommentDetail author='Maximillian' timesAgo = 'Today at 4:00PM' comment = 'Nice blog post!' image = {faker.image.avatar()} />
</ApprovalCard>
So technically props.children will be the <CommentDetail> component which can be rendered as JSX in the following statement because it is not just a simple object:
<div className="content">{props.children}</div>
From the Composition vs Inheritance documentation check especially the containment section which states:
Some components don’t know their children ahead of time. This is especially common for components like Sidebar or Dialog that represent generic “boxes”.
We recommend that such components use the special children prop to pass children elements directly into their output.
I hope this helps!

Related

How to display a particular component with different API data on different pages

I am new to react and there is this challenge that i am having,
I have slider created a component
import React from 'react'
import NextEvent from '../nextEvent/NextEvent'
import './slider.css';
function Slider(props) {
const {id, image, sub_title, title} = props;
return (
<main id='slider'>
<div className="slide" key= {id}>
<div className="slide-image">
<img src={image} alt="slider-background"/>
</div>
<h1>{title} </h1>
<h5>...{sub_title}</h5>
</div>
<div className="event-countdown">
<NextEvent/>
</div>
</main>
)
}
export default Slider
I need to have this banner component on almost all my pages, and on each of the pages, it comes with a
different information (image, title, subtitle)
the backend guy sent the api and i consumed, but the problem is that, if i consume the api on the
component directly, all the pages will have the same info on the banner component which is not what i want,
also consuming the API on the homepage seemed like it was not the right thing to do, so i created another component which
collects the Api and i then added that new component to my homepage.
now my question goes:
did i do the correct thing ?
if correct, does it mean i have to create new corresponding components that will receive the APi for
each page i want to display the banner just like i did for the homepage?
will i have to as the backend guy to create different apis for each of the pages in which the
component is to be displayed
if no please help me with an efficient way which i can inject data coming from the backend into a
component which will be displayed on different pages with different data
this is the new component i created for the APi consumption
import React, {useState, useEffect } from 'react'
import NextEvent from '../../components/nextEvent/NextEvent'
import axios from "axios";
import '../../components/slider/slider.css';
const sliderUrl = "*************************************"
function HomeSlider(props) {
const [sliderData, setSliderData] = useState([]);
const { image, sub_title, title} = props;
const getSliderContents = async () => {
const response = await axios.get(sliderUrl);
const content = response.data;
setSliderData(content);
}
useEffect(() => {
getSliderContents();
}, [])
// console.log("slider", sliderData)
if(sliderData) {
return (
<main id='slider'>
{sliderData.map((item) => {
return (
<div className="slide" key= {item.id}>
<div className="slide-image">
<img src={item.image} alt="slider-background"/>
</div>
<h1>{item.title} </h1>
<h5>...{item.sub_title}</h5>
</div>
)
})}
<div className="event-countdown">
<NextEvent/>
</div>
</main>
)
}
}
export default HomeSlider
this is the Homepage i displayed it
function HomePage() {
return (
<div>
<NavBar/>
<SecondaryMenu/>
<HomeSlider />
<FeaturedBox />
Please any help is appreciated, i have search all over but no one explains how to display
component with different data on different pages
So i just wanted to get back on this, i figured i have to setup a service point where i call the api and then consume the endpoints on each page as desired

how to load an image in another component by click event on sibling component in react

I am new to react and wanted to have a basic react app. I have two sibling components. In one component(ImagePreview), in that I have a list of images that I show as a list of thumbnails. I have another component (ImageReal) in which I which I want to show a full scale image.
I want to update the state of ImageReal such that when an image in the ImagePreview component is clicked, it updates the state in imageReal and that image would show up in ImageReal. I have looked on quite a few examples but I couldn't get my head around may be b/c I am new to this stuff.
import React from 'react';
import './styles/app.css';
import ImagePreview from './components/ImagePreview'
const ImageList = [{imagePreview: "/image/image1.jpg"},{imagePreview: "/image/image2.jpg"},{imagePreview: "/image/image3.jpg"}];
function App() {
return (
<div className="flex mb-4">
<div className="w-1/4 bg-gray-400 h-screen mx-2 my-1">
{ ImageList.map(image => (
<ImagePreview key={image.id} image={image} ></ImagePreview>
))}
</div>
<div className="w-3/4 bg-gray-500 h-screen mx-2 my-1">
</div>
</div>
);
}
export default App;
Image Preview Component
import React from 'react'
const ImagePreview = ({ image }) => {
let helloWorld = (e) => {
console.log('Hello world :', e.target);
}
return (
<div className="py-5 px-10">
<img src={image.previewURL} className="w-full" onClick={helloWorld}/>
</div>
);
}
export default ImagePreview
ImageReal Component
import React from 'react'
const ImageReal = ({ image }) => {
return (
<div className="py-5 px-10">
<img src={image.previewURL} className="w-full" />
</div>
);
}
export default ImageReal
If you want both the components to interact with each other then it's probably a good idea to manage state in a common parent ancestor component.
For more information please refer to this link:
https://reactjs.org/docs/lifting-state-up.html#lifting-state-up

React: How to hide a component by clicking on it?

I am trying to make a React component hidden by clicking on it but all I see online are explanations about event handlers on buttons and the like.
I'm using Next.js (but I don't think these means much for this).
This is my component:
import styles from './styles/popup.module.scss';
import React, { Component } from "react";
export default function Popup() {
return (
<div className={styles.popup}>
<div className={styles.popupInner}>
<h1>Temporary Closure</h1>
<p>
Following the Australian government’s directive to keep our nation safe and limit the spread of Coronavirus (Covid-19), it is with great sadness that we advise that SS will be temporarily closed, effective 23<sup>rd</sup> March 2020
</p>
</div>
</div>
)
}
Try setting a state on click of your component. Below code should work.
import styles from './styles/popup.module.scss';
import React, { Component } from "react";
export default function Popup() {
const [visible, setVisible] = React.useState(true);
if(!visible) return null;
return (
<div className={styles.popup} onClick={() => setVisible(false)}>
<div className={styles.popupInner}>
<h1>Temporary Closure</h1>
<p>
Following the Australian government’s directive to keep our nation safe and limit the spread of Coronavirus (Covid-19), it is with great sadness that we advise that sydney sauna will be temporarily closed, effective 23<sup>rd</sup> March 2020
</p>
<div className={styles.buttonContainer}><button className={styles.button}>Okay</button></div>
</div>
</div>
)
}
Hope it helps.
You could use a state property which tells you whether you should hide the component or not.
Based on that state, conditionally render another css class to the component you want to hide, using the classnames package (you will need to preinstall it npm install --save classnames)
import React, {useState} from 'react';
import classes from './Component.module.css';
import classNames from 'classnames';
const Component = props => {
const [show, setShow] = useState(true);
return (
<div className={classes.Component} onClick={() => setShow(!show)}>
<div
className={classNames( {
[classes.Show]: true,
[classes.Disappear]: !show
})}
>
{props.children}
</div>
</div>
);
};
export default Component;
In the Disappear css class, you can use whatever css properties you need to make your component disappear in a more elegant way, such as display: none; or visibility: hidden; (including transitions)
Of course, if what you are looking for is just not rendering the component at all, the standard "wrapping the div in an if statement" shown in the other answers is a perfectly valid solution.
You need to create a state variable which will determine to show popup or not.
You can achieve it by using useState hook.
import styles from './styles/popup.module.scss';
import React, { Component , useState } from "react";
export default function Popup() {
const [isPopupVisible,setPopupVisibility] = useState(true);
return (
<div className={styles.popup}>
{ isPopupVisible && (<div className={styles.popupInner}>
<h1>Temporary Closure</h1>
<p>
Following the Australian government’s directive to keep our nation safe and limit the spread of Coronavirus (Covid-19), it is with great sadness that we advise that SS will be temporarily closed, effective 23<sup>rd</sup> March 2020
</p>
<div className={styles.buttonContainer}><button className={styles.button} onClick={setPopupVisibility(false)}>Okay</button></div>
</div>)}
</div>
)
}

What is the best way to go about conditional rendering in the case of very similar components?

I render different landing pages based on whether the user is a professor, student, or not logged in. The landing pages are very similar; the only difference is the buttons displayed. I know I can go around this using inline conditions or simple if-else statements. However, I was wondering what the best practices are to implement conditional rendering in this case. I know higher order components (HOCs) can help but I was not sure if they are overkill in this particular case.
To be on the same page, here are the different Landing components that I currently render using if-else statements.
Landing.js (unlogged users):
import React from 'react';
import { withRouter } from 'react-router-dom';
import { compose } from 'recompose';
import { withEither } from '../../helpers/withEither';
import LandingStudent from './LandingStudent';
import LandingProfessor from './LandingProfessor';
import './Landing.css';
const Landing = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<h1 className="header-primary">
<span className="header-primary-main">
QME
</span>
<span className="header-primary-sub">
the best way to ask questions
</span>
</h1>
</div>
</div>
);
};
export default Landing;
LandingProfessor.js
import React from 'react';
import { withRouter } from 'react-router-dom';
import RaisedButton from 'material-ui/RaisedButton';
import './Landing.css';
const LandingProfessor = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<h1 className="header-primary">
<span className="header-primary-main">
QME
</span>
<span className="header-primary-sub">
the best way to ask questions
</span>
</h1>
<RaisedButton
className="btn-animated btn-landing"
label="Create Class"
onClick={() => history.push('/courses/new')}
/>
<RaisedButton
className="btn-animated btn-landing"
label="Dashboard"
onClick={() => history.push('/courses')}
/>
</div>
</div>
);
};
export default withRouter(LandingProfessor);
LandingStudent.js
import React from 'react';
import { withRouter } from 'react-router-dom';
import RaisedButton from 'material-ui/RaisedButton';
import './Landing.css';
const Landing = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<h1 className="header-primary">
<span className="header-primary-main">
QME
</span>
<span className="header-primary-sub">
the best way to ask questions
</span>
</h1>
<RaisedButton
className="btn-animated btn"
label="Join Class"
onClick={() => history.push('/courses/join')}
/>
</div>
</div>
);
};
export default withRouter(Landing);
A 'trick' could be to append a className on the root div for landscape, namely 'student' 'professor' or 'logout' and use css display: none to hide unwanted items in each scenarios
Another approach would be to keep your 3 components but delegate all the rendering to a common 'LandingRenderer' component that would accept boolean properties like 'showJoinClassButton' 'showCreateButtonButton' etc.
Then your 3 components render would look like domething like this
LandingProfessor: (props) => (
<LandingRenderer
showJoinClassButton={false}
showCreateClassButton={true}
...
{...props} />
)
I would opt for composition over inheritance here, meaning create many reusable components and compose your top-level components with those reusable ones rather than make your top-level components inherit from one common component type. If you follow the latter approach, you might end up with a laundry list of props that I argue is questionably better than what you have right now.
To start, you could componentize your header:
Header.js
export default function Header(props) {
return (
<h1 className="header-primary">
{props.children}
</h1>
);
}
Header.Main = function HeaderMain(props) {
return (
<span className="header-primary-main">
{props.children}
</span>
);
};
Header.Sub = function HeaderSub(props) {
return (
<span className="header-primary-sub">
{props.children}
</span>
);
};
and use it in Landing.js:
import Header from './Header.js';
const Landing = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<Header>
<Header.Main>QME</Header.Main>
<Header.Sub>the best way to ask questions</Header.Sub>
</Header>
</div>
</div>
);
}
I don't think hoc in this particular case is an overkill.
I think whenever you can use hoc for better readability and useability, use it.
Using some of recompose hocs (you should install recompose: npm install recompose)
export const renderByConditions = (...conditions) => compose(
...conditions.map(condition =>
branch(condition[0], (condition[1] && renderComponent(condition[1])) || renderNothing, condition[2] && renderComponent(condition[2]))
)
)
You should pass arrays with the signature:
[condition, left (if the condition is true), right (else)]
condition - (ownProps) => youCondition
left - Component | empty
right - Component | empty
when left is empty - if the condition is true, it will render null.
when right is empty - if the condition is not true, it will render the component we wrapped
Then you can use the hoc:
renderByConditions(
[props => props.landing, props => <Landing {...props}/>],
[props => props.proffesor, LandingProfessor],
[props => props.student, LandingStudent],
[Component => Component, DefaultComponent]
)
I would reccommend start using recompose hocs, they are great!

Trouble with functional creation and rendering of div in sub of a sub

I'm sure im missing some key element of understanding because this file gets exported and used in another file and then that is exported to another file and then that last file in the chain is what is sent to react.DOM. but why can't I make my components in a function in this file and have them be rendered. I'm not understanding something about the chain and how many exported files you can have and how i guess nested they can be.... help please. Cause if I do this at the surface level of the file chain it works fine but not this far down...
import React, { Component } from 'react';
import './Css_files/OfficeComponent.css';
class OfficeComponent extends Component {
pic_span_nurse(props){
return(
<div className="row box_infoz">
<div className="col-xs-3">
<h1>picture</h1>
</div>
<div className="col-xs-9">
<h5>So this has noew changed to the office part where we have staff in this box and directions on the bottom</h5>
</div>
</div>
);
}
render() {
return (
<div>
<pic_span_nurse/>
</div>
);
}
}
export default OfficeComponent;
I am very surprised doing <pic_span_nurse/> works at all, in any context.
I think you're mistaking the concept of what a Component is. A method inside a Component is not considered a component. It is still a method. Which means you have to render pic_span_nurse like you would when you return a method. This should definitely work:
{this.pic_span_nurse()}
The curly braces mean it's JavaScript code that should be interpreted, rather than literal text.
Also, JavaScript style guides encourage (read: must) naming to be done in camelcase, not underscores.
You can either create a separate component and use it in your code.
import React,{Component} from 'react';
import './Css_files/OfficeComponent.css';
const Pic_span_nurse =(props)=>{
return(
<div className="row box_infoz">
<div className="col-xs-3">
<h1>picture</h1>
</div>
<div className="col-xs-9">
<h5>So this has noew changed to the office part where we have staff in this box and directions on the bottom</h5>
</div>
</div>
);
}
class OfficeComponent extends Component {
render() {
let compProps = {};//the props of the Pic_span_nurse component
return (
<div>
<Pic_span_nurse {...compProps}/>
</div>
);
}
}
export default OfficeComponent;
Or you can use a function call to render the necessary html.
import React, { Component } from 'react';
import './Css_files/OfficeComponent.css';
class OfficeComponent extends Component {
pic_span_nurse=(props)=>{
return(
<div className="row box_infoz">
<div className="col-xs-3">
<h1>picture</h1>
</div>
<div className="col-xs-9">
<h5>So this has noew changed to the office part where we have staff in this box and directions on the bottom</h5>
</div>
</div>
);
}
render() {
return (
<div>
{this.pic_span_nurse(this.props)}
</div>
);
}
}
export default OfficeComponent;

Categories

Resources