How can I pass attributes to child components via cloneElement? - javascript

I have a wrapping component that is supposed to pass attributes down to its children. So everywhere I need theme'ing, it will add the right attributes and props. When I use cloneElement to create props, I see those props as attributes on child divs, but not child components.
I want the divs wrapping text to have the same attributes. This is how the below renders:
I'm not sure why React knows to do this for top level HTML elements, but not the top level HTML element of a component.
The playground for my code is here: https://codesandbox.io/s/react-playground-forked-t17k3u?file=/index.js
import React from "react";
import ReactDOM from "react-dom";
function ThemeProvider(props) {
const { children } = props;
const theme = "dark";
return (
<div>
{React.cloneElement(children, {
theme,
})}
</div>
);
}
function InnerBits() {
return (<div>I am a child component and I have no attributes passed</div>)
}
class App extends React.Component {
render() {
return (
<div>
<ThemeProvider>
<div foo="bar">I am a div inside ThemeProvider. I have both "foo" from the div and "bar" from cloneElement</div>
</ThemeProvider>
<br /><br />
<ThemeProvider>
<InnerBits foo="bar" />
</ThemeProvider>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));

Related

Difference between importing module and passing it as a prop vs importing it from within the module that actually uses it

Consider this first approach. It imports SomeMassiveModule in the Parent component and then passes it down to the Child components.
// Parent.js
import React from 'react'
import * as SomeMassiveModule from 'some-massive-module'
export default function Parent() {
return (
<Child SomeMassiveModule={SomeMassiveModule} />
<Child SomeMassiveModule={SomeMassiveModule} />
<Child SomeMassiveModule={SomeMassiveModule} />
)
}
// Child.js
import React from 'react'
export default function Child({ SomeMassiveModule }) {
return (
<div>
{SomeMassiveModule.SomeComponent}
</div>
)
}
Now, consider this second approach, which imports SomeMassiveModule in the Child component and then uses it.
// Parent.js
import React from 'react'
export default function Parent() {
return (
<Child />
<Child />
<Child />
)
}
// Child.js
import React from 'react'
import * as SomeMassiveModule from 'some-massive-module'
export default function Child() {
return (
<div>
{SomeMassiveModule.SomeComponent}
</div>
)
}
Is there a difference in performance between these two approaches? Does the bundling now happen three times in the second approach instead of just the once?
You can use the first approach when you want to pass different values in the props to the Child component. If it is going to be the constant (won't change in future) import option i.e 2nd approach seems better than the first one.

Access form elements of wrapped component in HOC?

I want to build an HOC, in which i want to access the form elements present in the Wrapped Component.
import React from 'react';
const hoc = (WrappedComponent) => (props) => {
return(
<React.Fragment>
<WrappedComponent { ...props }></WrappedComponent>
</React.Fragment>
)
};
export { hoc };
In this HOC, i want access each element from the WrappedComponent. Can anyone guide me the way to implement this functionality

Passing data from child to parent component not working in react

I am passing data from my child component (Header component) to my parent component (App component). My idea: when I click on my creating button in header component I want to send information to app component to hide header and footer. Here is code example.
Header.jsx:
import React, { Component } from 'react';
import { Nav, Navbar, Button } from "react-bootstrap";
class Header extends Component {
constructor(props) {
super();
this.state = {
showHeaderAndFooter: false
};
}
onChangeChildComponent = () => {
this.props.changeVisibilityOfHeaderAndFooter(this.state.showHeaderAndFooter);
}
render() {
return (
<Navbar bg="dark" variant="dark">
<Nav className="mr-auto">
<Button href="/createGym" onClick={this.onChangeChildComponent.bind(this)}> Create </Button>
</Nav>
</Navbar>
);
}
}
export default Header;
App.js:
import React, { Component, Fragment } from 'react';
import './App.css';
import './components/header';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Header from './components/header';
import Footer from './components/footer';
import Home from './components/home';
import CreateGym from './components/create-gym';
import Login from './components/authentication/login';
class App extends Component {
constructor() {
super();
this.state = {
showHeaderAndFooter: true
};
}
onChangeChildComponent (showHeaderAndFooter) {
this.setState(
{
showHeaderAndFooter: showHeaderAndFooter
});
}
Container = () => (
<div>
{ this.state.showHeaderAndFooter &&
<Header
changeVisibilityOfHeaderAndFooter = {this.onChangeChildComponent.bind(this)}
/>
}
<div className="container">
<Route exact path='/' component={Home} />
<Route path="/createGym" component={CreateGym} />
<Route path="/login" component={Login} />
</div>
{ this.state.showHeaderAndFooter && <Footer /> }
</div>
)
render() {
console.log(this.state.showHeaderAndFooter);
return (
<BrowserRouter>
<Switch>
<Fragment>
{ <Route component={this.Container}/> }
</Fragment>
</Switch>
</BrowserRouter>
);
}
}
export default App;
The problem is because my code is entering App constructor twice. At the first time, everything is fine, but on the second time, this boolean showHeaderAndFooter is again set to true because that is the default value. Any idea how to solve this?
you shouldn't be passing from child > parent. react is uni-directional (that means data can only flow in one way, and that way is downwards)
to achieve this, move the state into the parent
class Parent extends React = {
this.state = { showHeaderAndFooter: false }
functionToToggleShowing = () => {
this.setState({showHeaderAndFooter: !this.state.showHeaderAndFooter})
}
render() {
return(
<Child showHeaderAndFooter={this.state.showHeaderAndFooter} functionToToggleShowing={functionToToggleShowing} />
)
}
}
that is pseduo code but essentially move the state to the parent and pass down the state to the child as well as a way to change that state
React allows passing of control from child to parent by means of props.
Here is the 3-step procedure to make it work:
Step 1: Create a data member in Parent, which manipulates parent state.
Step 2: Send the parent's data member as a prop to the child.
Step 3: In the child, Call the prop sent by parent for responding to an event.
Here is a demonstration of passing control from child to parent, using the above technique:
The example has 2 components -
Parent component is App.jsx
Child component is Header.jsx
Parent has a state-manipulating data member called 'hideHeader'.
Parent passes hideHeader as a prop called onClick to the child.
Child calls the prop sent by parent, in response to its onClick event.
Parent component - App.jsx
import React, { Component } from 'react'
import Header from "./Header";
export default class App extends Component {
constructor() {
super();
this.state = {
showHeader: true
}
}
{/* A state-manipulating data member */}
hideHeader = () => {
this.setState({showHeader: false})
}
render() {
return (
<div>
{/* State-manipulating data member sent as prop to child */}
{ this.state.showHeader &&
<Header onClick={this.hideHeader} />
}
</div>
)
}
}
Child component - Header.jsx
import React, { Component } from "react"
export default class Header extends Component {
render() {
return (
<div>
<h1>Header Demo</h1>
{/* Call the prop sent by parent when button is clicked */}
<button onClick={this.props.onClick}>Hide</button>
</div>
)
}
}
Output:
Before 'Hide' button is clicked:
After 'Hide' button is clicked:

Getting the value of Props parameter as undefined (ReactJS)

I'm having an attribute in one of the components and when I'm trying to access that attribute via props, I'm getting its value as undefined.
Below is the piece of code where I'm making use of the component and passing the required attribute.
import React, { Component } from "react";
import PageNotFound from "./pages/page-not-found";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import BookSectionPage from "./pages/books-section";
import BookDetails from "./pages/book-details";
class App extends Component {
render() {
return (
<div>
<BrowserRouter>
<Switch>
<Route path="/" exact component={BookSectionPage}/>
<Route path="/book/category/:categoryName" exact render = { (props) => {
return <BookSectionPage title = "JavaScript" /> // This is the component
}} />
<Route path="/book/:bookID" exact component={BookDetails} />
<Route component={PageNotFound} />
</Switch>
</BrowserRouter>
</div>
);
}
}
export default App;
Below is the code for the component where I'm trying to access the above-mentioned attribute via Props but getting its value as undefined.
import React from "react";
import Header from "../components/header/header";
import Footer from "../components/Footer/footer";
import BookSectionComponent from "../components/books-section/books-section";
const BookSectionPage = (Props) => {
let books=[1,2,3,4,5,6];
console.log(Props.title); // Here instead of printing the value of attribute, it's showing undefined.
return (
<div className="has-fixed-footer">
<Header />
<BookSectionComponent title = {Props.title} books = {books} />
<Footer />
</div>
);
};
export default BookSectionPage;
Any help would be highly appreciated. Thanks!
From Parent to Child Using Props
App
└── Parent
├── Child1
Most easiest direction of data flow in React and basic example.
Parent Component
class Parent extends React.Component {
state = { title : "JavaScript" }
render() {
return (
<div>
<Child1 dataFromParent = {this.state.title} />
</div>
);
}
}
Child Component
class Child1 extends React.Component {
render() {
return (
<div>
The data from parent is:{this.props.dataFromParent}
</div>
);
}
}

React component printed twice on the screen

My React component named 'Person' prints twice on the screen,once without props & once with props.
//App.js
import React, { Component } from 'react';
import './App.css';
import Person from './Person/Person'
class App extends Component {
render() {
return (
<div className="App">
<h1>Hello I am Arjun</h1>
<Person>But What's The Language?</Person>
<Person technology="React" syntax="JSX"/>
</div>
);
}
}
export default App;
//Person.js
import React from 'react'
const person = (props) => {
return (
<div>
<p>{props.children}</p>
<p>The technology used is {props.technology} & the syntax
is {props.syntax}</p>
</div>
)
};
export default person
And this is the output :
How can I solve this problem?
Other answers show you how you should do that and told the main reason. I will try to explain it a little bit more. You are rendering your component twice and you have three lines because of the structure of your component.
First render:
<Person>But What's The Language?</Person>
Here you are rendering the component with a child. Your child here is "But What's The Language?" string. You don't pass any other prop other then child. Your component shape is:
<p>{props.children}</p>
<p>The technology used is {props.technology} & the syntax is {props.syntax}</p>
First line is coming from the children prop. Second line is your paragraph with other props. So, in the first render you don't have those props, hence rendering as empty for technology and syntax.
Your second render:
<Person technology="React" syntax="JSX"/>
Here you don't have any child. It means no <Person>Child</Person> shape but just <Person /> For this render you have other props. Third line is coming from this render with technology and syntax props. Hence you don't have any children prop, first line is empty.
The issue is that you in fact render your component twice. You should have only one <Person /> Tag in your App component:
//App.js
import React, { Component } from 'react';
import './App.css';
import Person from './Person/Person'
class App extends Component {
render() {
return (
<div className="App">
<h1>Hello I am Arjun</h1>
<Person technology="React" syntax="JSX">But What's The Language?</Person>
</div>
);
}
}
export default App;
You are rendering your component twice, to obtain the desired output you could do something like this:
//App.js
import React, { Component } from 'react';
import './App.css';
import Person from './Person/Person'
class App extends Component {
render() {
return (
<div className="App">
<h1>Hello I am Arjun</h1>
<Person technology="React" syntax="JSX">
But What's The Language?
</Person>
</div>
);
}
}
export default App;
that way, your component renders its children "But What's The Language?", and receives the technology and syntax properties for the second line.

Categories

Resources