Why won't my nested React components render? - javascript

I'm having a problem with my React component. The nested children of my component ControlPanel don't seem to be rendering. Here is my code:
class App extends Component {
render() {
return (
<div className="App">
<ControlPanel>
<CustomerDisplay />
</ControlPanel>
</div>
);
}
}
I have the following two lines at the top of this file:
import ControlPanel from './components/control_panel';
import CustomerDisplay from './components/customer_display';
And here is my ControlPanel Component:
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './styles.scss';
const ControlPanel = () => {
return (
<div className="control_panel" id="control_panel">
</div>
);
}
export default CSSModules(ControlPanel, styles);
I have tried:
Calling the component as a full HTML tag (opening & closing)
Nesting the CustomerDisplay component in the ControlPanel component (in the ControlPanel's index.jsx file)
I know that nesting component's is possible. I've seen it done. For some reason it just won't work for me.

To allow components to contain children and render them correctly, you have to use this.props.children. This is passed to all components with children as a prop and contains the children of the component, as explained by the React documentation:
Containment
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:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
This lets other components pass arbitrary children to them by nesting the JSX
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
As described in the documentation, some components don't know their children ahead of time -- they may be generic wrappers or boxes of content that vary, which is what your ControlPanel is. So, to render the children of your component, you must render the children from the children prop explicitly in the parent's render method. Thus, apply it like this to your code:
const ControlPanel = (props) => {
return (
<div className="control_panel" id="control_panel">
{props.children}
</div>
);
}
Notice how props.children is rendered (not this.props.children because it is a function component).

You can access the nested elements through props. So in your case do this:
const ControlPanel = (props) => {
return (
<div className="control_panel" id="control_panel">
{props.children}
</div>
);
}

You need to render the children in ControlPanel
const ControlPanel = ({ children }) => {
return (
<div className="control_panel" id="control_panel">
{children}
</div>
);
}

function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
export default function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
Anything inside the<FancyBorder>JSX tag gets passed into the FancyBorder component as childrenprop. Since FancyBorder renders {props.children} inside a <div>, the passed elements appear in the final output.
This is what I was looking after, check it out here
https://reactjs.org/docs/composition-vs-inheritance.html

Your App.js (I understand that it is your JSX Index) should be:
import React from 'react';
import ReactDOM from 'react-dom';
export default class App extends Component {
render() {
return (
<div className="App">
<ControlPanel>
<CustomerDisplay />
</ControlPanel>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('YOUR_ROOT_ID'));
Try to add export default before class (in all your components).

Related

React - functional component container

I would like to create a container component in my application. I don't know if this is possible or if it's a good idea at all. I would be so very comfortable.
From this:
export default function App(){
return(
<div className={styles.CardsContainer}>
<CardPrice price={700}/>
<CardPrice price={3000}/>
<CardPrice price={5000}/>
</div>
)
}
I want to do it:
export default function App(){
return(
<CardsContainer>
<CardPrice price={700}/>
<CardPrice price={3000}/>
<CardPrice price={5000}/>
</CardsContainer>
)
}
I do this in CardsContainer:
export default function CardsContainer(){
return(
<div className={styles.CardsContainer}>
</div>
)
}
Obviously it doesn't work) But I don't know how to wrap properly.
I don't want to put components with CardPrice in a CardContainer component. I want to wrap in App component
u can use this, but you will need to take de props.chidlren inside the container.
export default function CardsContainer({children}){
return(
<div className={styles.CardsContainer}>
{children}
</div>
)
}
but I think will be better if u use the styled component to create de container
You need to pass the elements as children to the CardsContainer and in CardsContainer component you can access it using the children prop. For a better understanding, you could refer these docs: https://reactjs.org/docs/composition-vs-inheritance.html#containment
export default function CardsContainer({children}){
return(
<div className={styles.CardsContainer}>
{children}
</div>
)

How can I call Child function from Parent Component in React

Good day. I have three components. A Master Component, A TopBar and a Child component (Refers to the rest of my application). The Master is a layout wrapper that nests the Child Component. The Top Bar component is used inside the Master Component so that it is constant througout the site.
The Challenge is that, when a button is clicked on the Top Bar, I want a function to be called inside the Child Component.
What is the best possible way to achieve this
Master.js
import React from 'react'
import TopBar from './TopBar'
const Master = (props) => {
function handleFunc (childFunc) {
setChildFunction(childFunc);
}
return (
<>
<div>
<TopBar onHandle={handleFunc}/>
{props.children}
</div>
</>
)
}
export default Master
TopBar.js
import React from 'react'
const TopBar = ({onHandle}) => {
const childFunc = React.useRef(null)
function handleClick (){
childFunc.current();
onHandle(childFunc);
}
return (
<div>
<button onClick={() => handleClick()}>Click on Me</button> {/* Triggers the alert() function in Child.js */}
</div>
)
}
export default TopBar
Child.js
import React from 'react'
import Master from './Master'
const Child = () => {
function alert(){
console.log("This function is called when thew Top Bar Button is clicked");
}
return (
<div>
<Master>
Child Contents
</Master>
</div>
)
}
export default Child
What is the best way to achieve this.
I initially wanted to pass the ref object to the Master component so it can be passed down to the Child as shown above, but I keep getting errors (that "childFunc" is not a function).
Thanks in advance
You can pass your Child function to the Master component, then from the Master Component to TopBar component, that will cause props drilling but it will solve your problem,
Child.js
import React from 'react'
import Master from './Master'
const Child = () => {
function alert(){
console.log("This function is called when thew Top Bar Button is clicked");
}
return (
<div>
<Master func={alert}>
Child Contents
</Master>
</div>
)
}
export default Child
Master.js
import TopBar from './TopBar'
const Master = ({func}) => {
return (
<>
<div>
<TopBar func={func}/>
{props.children}
</div>
</>
)
}
export default Master
TopBar.js
import React from 'react'
const TopBar = ({func}) => {
return (
<div>
<button onClick={() => func()}>Click on Me</button>
</div>
)
}
export default TopBar

Rendering Multiple Instances of the Same Component - React

I want to render multiple instances of the same component. Here is the component that will be repeated (not my actual component, just an example):
import React from 'react';
function Repeated () {
return (
<div></div>
)
}
export default Repeated;
And the component that will repeat the other (again, just an example):
import React from 'react';
import Repeated from './Component1';
function OtherComp () {
return (
<div>
<Repeated />
<Repeated />
<Repeated />
<Repeated />
</div>
)
}
export default OtherComp;
Is there any way I can add the "Repeated" component multiple times through something like a loop, instead of having to copy and paste the component multiple times? Thanks :)
You can create a new array of desired length and map it to the components. However, you have to add a key to every one of them or ignore a warning:
With warning:
return (
<div>
{Array(4).fill(<Repeated />)}
</div>
)
Mapping to keys:
return (
<div>
{Array(4).fill(true).map((_, i) => <Repeated key={i} />)}
</div>
)
If you have an array for example and want to render each element as Repeat you can write
import React from 'react';
import Repeated from './Component1';
function OtherComp () {
// get array via fetch or something else
return (
<div>
{ array.map(item => <Repeated key={a-unique-key} item={item} />) }
</div>
)
}
export default OtherComp;
You can loop through the array of props you want to render via the Repeated components, provided that each of the rendered components has a unique key
The solution provided by #Jakub should work fine

How can I call the function of a parent component inside of a child component?

First off, I'm newer to react so please feel free to critique any sort of architectural problems I have, I feel like there's a better way to write this but I've been struggling for an hour trying to get it to work.
Parent Element (TileGrid):
import React, { Component } from 'react'
import Tile from './Tile.js';
export default class TileGrid extends Component {
constructor(props) {
super(props);
}
updateTileData(value) {
{/* Do something with value here */}
}
render() {
this.test()
return (
{/* The Tile component is a custom button with some richer features. */}
<Tile name='Dog' image='dog' />
)
}
}
Child Element (Tile):
import React from 'react';
import ButtonBase from '#mui/material/ButtonBase';
import './styles/Tile.css';
function Tile(props) {
return(
<div className="Tile">
<ButtonBase onClick={/* This is where the Tile needs to call updateTileData in TileGrid */}>
Button
</ButtonBase>
</div>
);
}
export default Tile;
So there's a function inside of TileGrid called updateTileData that is going to take some data and use it to update the state of TileGrid. The Tile component exists as a child within the TileGrid component. I've tried all sorts of stuff on here but this seems like a simple task to do, is there a better way to write this functionality? I'm not tied down to anything in the code, please tell me if there's a better way to do this. I'm still learning this framework and this issue has me hung up. Thanks, Archer.
pass the function from parent to child as a prop example :
<Child func={this.functionInParent} />
props.func() //called inside child
you need to pass a prop to child Element and Call it.
import React, { Component } from 'react'
import Tile from './Tile.js';
export default class TileGrid extends Component {
updateTileData(value) {
{/* Do something with value here */}
}
render() {
this.test()
return (
{/* The Tile component is a custom button with some richer features. */}
<Tile name='Dog' image='dog' setValue={this.updateTileData} />
)
}
}
child Element :
import React from 'react';
import ButtonBase from '#mui/material/ButtonBase';
import './styles/Tile.css';
function Tile(props) {
return(
<div className="Tile">
<ButtonBase onClick={()=>props.setValue("your value")}>
Button
</ButtonBase>
</div>
);
}
export default Tile;

Passing props twice for a Higher Order Component?

I'm building a webpage and realized a common style shared by each component (same background, border, and title style). So I thought I should make an HOC which accepts the inner content of each component as well as a title, and returns an outer component which wraps this inner component and heading.
At first I ran into a lot of issues trying to get this to work, being new to React, but now it's finally working but I still don't understand how.
Here is my HOC
const BaseBlock = (WrappedComponent) => {
return class BaseBlock extends Component {
render () {
return (
<div className={styles['base-block']}>
<div className={styles['container']}>
<div className={styles['base-block-head']}>
{ this.props.title }
</div>
<div className={styles['base-block-body']}>
<WrappedComponent {...this.props} />
</div>
</div>
</div>
)
}
}
}
export default BaseBlock
This is the WrappedComponent:
const HighlightsBlock = (props) => {
return <ListsComponent items={props.items} />
}
export default BaseBlock(HighlightsBlock)
And this is the ListsComponent
const ListsComponent = (props) => {
if (props.items) {
return (
<ul className={styles['styled-list']}>
{props.items.map((item, idx) => {
return (
<li key={idx} className={styles['styled-list-item']}>{item}</li>
)
})}
</ul>
)
} else return (
<h3>No highlights</h3>
)
}
export default ListsComponent
And this is how I'm using the component in my app:
<HighlightsBlock items={this.getHighlights()} title='Highlights' />
Now, I can see the HighlightsBlock component receiving props twice (Once when I'm using it in my App with props, and once inside the HOC Baseblock as WrappedComponent ). If I remove props from either of these places it stops working. I don't understand how this is working.
When you render <HighlightsBlock items={this.getHighlights()} title='Highlights' /> you are actually rendering the component returned by HOC which in turn renders your actually HighlightsBlock component as <WrappedComponent {...this.props} />
You can think of HighlightsBlock component to be nested two level deep and hence you need to pass on the props to it, firstly as {...this.props} from within HOC and then receive it as props in functional component
This is because of this.getHighlights() in this line,
<HighlightsBlock items={this.getHighlights()} title='Highlights' />
Every time you pass props to child component this function is getting executed.
To solve this issue, maintain a state value in your parent component and set that value in getHighlights function like,
getHighlights(){
//you logic to get data
this.setState({items:data.items}); //considering `data` is object which has `items`
}
Now you can pass items like,
<HighlightsBlock items={this.state.items} title='Highlights' />

Categories