Passing props.children to a conditionally rendered component - javascript

I have Product component that renders a modal component based on a showModal boolean value in the Product component state. This is the render method in Product
render() {
const { .... } = this.props.data;
return (
<>
{this.state.showModal && (
<Modal
product={this.props.data}
toggleModal={this.onToggleModal}
addToCart={this.props.onAddToCart}
/>
)}
<li className="product-body">
<img
src=".."
onClick={this.onToggleModal}
/>
<h2>{...}</h2>
<h3>{..}</h3>
<h3>{..}</h3>
<h2 className="product-price">{...}</h2>
<button type="button" onClick={this.props.onAddToCart}>
Buy now
</button>
</li>
</>
);
}
I want to pass down the content inside li to the Modal.How can I make use of props.children in this case for the Modal component, so I dont have to pass down the data to be displayed as props ?

If Modal is another component then you can pass the list as
render() {
const { .... } = this.props.data;
return (
<>
{this.state.showModal && (
<Modal
product={this.props.data}
toggleModal={this.onToggleModal}
addToCart={this.props.onAddToCart}
>
<ListItems {...this.props}/>
</Modal>
)}
<ListItems {...this.props}/>
</>
);
}
Let the ListItems be a separate component as
class ListItems extends React.Component {
render() {
return(
<li className="product-body">
<img
src=".."
onClick={this.onToggleModal}
/>
<h2>{...}</h2>
<h3>{..}</h3>
<h3>{..}</h3>
<h2 className="product-price">{...}</h2>
<button type="button" onClick={this.props.onAddToCart}>
Buy now
</button>
</li>
)}
}

class ListItems extends React.Component {
render(){
return(
<li className="product-body">
<img
src=".."
onClick={this.onToggleModal}
/>
<h2>{...}</h2>
<h3>{..}</h3>
<h3>{..}</h3>
<h2 className="product-price">{...}</h2>
<button type="button" onClick={this.props.onAddToCart}>
Buy now
</button>
</li>
)
}
}
class Wrapper extends React.Component {
render(){
return (
<Modal
product={this.props.data}
toggleModal={this.onToggleModal}
addToCart={this.props.onAddToCart}
>
<ListItems />
</Modal>
)
}
}
class Modal extends React.Component {
render(){
return (
<div>
<h1>Modal Title</h1>
<div>{this.props.children}</div>
</div>
)
}
}

Related

React JS - How does 2 separated components able to receive 1 same state?

I am a beginner in using the React JS framework. Based on the official React JS documentation, an example is given for changing the state of a component that has a connected hierarchy. But in my case this time I split the components for Header and Main separately.
index.js
ReactDOM.render(
<React.StrictMode>
<Header />
<Main />
</React.StrictMode>,
document.getElementById('root')
);
In the Header component I also have another sub component that functions to activate / deactivate the sidebar which is also a sub menu for the Main component.
Header.js
import { BtnSidebarOnClick } from './Sidebar';
const Header = () => {
return (
<header className="header">
<div className="header__logo">
<BtnSidebarOnClick />
<div className="header__logo_img">
<a className="link"
href="/">
<img src=""
alt="Schedule App" />
</a>
</div>
</div>
<nav className="header__nav">
...
</nav>
</header>
);
}
export default Header;
Main.js
import { Sidebar } from './Sidebar';
const Main = () => {
return (
<main className="main">
<Sidebar />
<div className="main__content">
...
</div>
</main>
);
}
export default Main;
Notice that the BtnSidebarOnClick and Sidebar components are not connected. In my case, this time I want to make the Sidebar component accept state to detect whether the button contained in the BtnSidebarOnClick component is clicked / not.
Sidebar.js
class BtnSidebarOnClick extends React.Component {
constructor(props) {
super(props);
this.state = { onClick: false };
}
handleClick() {
this.setState(state => ({ onClick: !state.onClick }));
}
render() {
return (
<div className="header__logo_btn">
<div className="button button--hover button--focus"
role="button"
tabIndex="0"
onClick={this.handleClick.bind(this)}>
<i className="material-icons">menu</i>
</div>
</div>
);
}
}
const Sidebar = () => {
return (
<div className="main__sidebar"> {/* set style if BtnSidebarOnClick clicked */}
<div className="main__sidebar_menu">
<div className="tag-link">
<a className="link link--hover link--focus link--active"
href="/">
<i className="material-icons">insert_drive_file</i>
<span className="link-title">Files</span>
</a>
</div>
</div>
</div>
);
}
export { Sidebar, BtnSidebarOnClick };
So how do you set these two components to receive the same state?
TLDR; You should pull out the button state into the parent and pass it into the children component.
By the way, it is a common way to have file App.js for your main Application file. In your case, it should be like this:
index.js
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = { isClicked: false };
}
handleClick() {
this.setState(state => ({ isClicked: !state.isClicked }));
}
render() {
return (
<div>
<Header onClick={this.handleClick} /> // --> notice
<Main isClicked={this.state.isClicked} /> // --> notice
</div>
)
}
}
Header.js
import BtnSidebar from './BtnSidebar';
const Header = (props) => {
return (
<header className="header">
<div className="header__logo">
<BtnSidebar onClick={props.onClick} /> // --> notice
<div className="header__logo_img">
<a className="link"
href="/">
<img src=""
alt="Schedule App" />
</a>
</div>
</div>
<nav className="header__nav">
...
</nav>
</header>
);
}
Main.js
import Sidebar from './Sidebar';
const Main = (props) => {
return (
<main className="main">
<Sidebar isClicked={props.isClicked} /> // --> notice
<div className="main__content">
...
</div>
</main>
);
}
BtnSidebar.js
const BtnSidebar = (props) => {
return (
<div className="header__logo_btn">
<div className="button button--hover button--focus"
role="button"
tabIndex="0"
onClick={props.onClick} // --> notice
>
<i className="material-icons">menu</i>
</div>
</div>
);
}
Sidebar.js
const Sidebar = (props) => {
return (
<div
className={
props.isClicked ? 'main__sidebar-clicked' : 'main__sidebar' // --> notice
}
>
<div className="main__sidebar_menu">
<div className="tag-link">
<a className="link link--hover link--focus link--active"
href="/">
<i className="material-icons">insert_drive_file</i>
<span className="link-title">Files</span>
</a>
</div>
</div>
</div>
);
}

React doesn't render unless I add a console.log() instruction

I have these two React components:
class Record extends React.Component {
render() {
...
return (
<div className="animated slideInUpTiny animation-duration-3">
{
showMessage && currentRecord &&
<Alert />
}
<div className="row">
<CardBox styleName="col-lg-12">
<AttrActions module={this.props.module} app={this.props.app} set={currentCatalog} record={currentRecord} accessCase="controls"/>
<AttrForm
fields_meta={alertedFieldsMeta}
currentForm={this.getCurrentForm()}
originalForm={this.state.originalForm}
reportChange={this.catchChange}
fetchList={fetchList}
hierarchy={this.state.hierarchy}
catalogsList={catalogsList}
fetchRecord={fetchRecord}
searchInCatalog={searchInCatalog}
/>
</CardBox>
</div>
<div className="crud-footer">
<ControlButtons />
</div>
</div>
);
}
}
class AttrActions extends React.Component {
render() {
console.log(this.props);
switch (this.props.module) {
case 'Inventory': {
return (
<InventoryActions {...this.props} />
);
}
default: {
return null;
}
}
}
}
The problem is that component AttrActions is never rendered unless I add a console.log() instruction in component Record, like this:
return (
<div className="animated slideInUpTiny animation-duration-3">
{
showMessage && currentRecord &&
<Alert />
}
<div className="row">
<CardBox styleName="col-lg-12">
{console.log('here')}
<AttrActions module={this.props.module} app={this.props.app} set={currentCatalog} record={currentRecord} accessCase="controls"/>
<AttrForm
fields_meta={alertedFieldsMeta}
currentForm={this.getCurrentForm()}
originalForm={this.state.originalForm}
reportChange={this.catchChange}
fetchList={fetchList}
hierarchy={this.state.hierarchy}
catalogsList={catalogsList}
fetchRecord={fetchRecord}
searchInCatalog={searchInCatalog}
/>
</CardBox>
</div>
<div className="crud-footer">
<ControlButtons />
</div>
</div>
);
Only in this case, I get the console.log(this.props) in the console. If I don't add this instruction, component AttrActions is never rendered.
EDIT
I found that when I put each component on different CardBox, the problem disappears, but I would like them both to be contained together:
return (
<div className="animated slideInUpTiny animation-duration-3">
{
showMessage && currentRecord &&
<Alert />
}
<div className="row">
<CardBox styleName="col-lg-12">
<AttrActions module={this.props.module} app={this.props.app} set={currentCatalog.name} record={currentRecord} accessCase="controls"/>
</CardBox>
<CardBox styleName="col-lg-12">
<AttrForm
fields_meta={alertedFieldsMeta}
currentForm={this.getCurrentForm()}
originalForm={this.state.originalForm}
reportChange={this.catchChange}
fetchList={fetchList}
hierarchy={this.state.hierarchy}
catalogsList={catalogsList}
fetchRecord={fetchRecord}
searchInCatalog={searchInCatalog}
/>
</CardBox>
</div>
<div className="crud-footer">
<ControlButtons />
</div>
</div>
);

onClick event never fires in React Child

I have a React-Redux application and I'm having issues launching a function on component click. When the logout button is clicked, it should trigger onClick={this.handleLogout}. However, Redux dev tools and adding a console log in the handleLogout prove that the function never executes.
Here are the two components, a standard navigation bar and a custom button component. I've left out some imports to shorten the code.
Navigation Bar
class Navigation extends React.Component {
handleLogout = () => {
this.props.logOut();
this.props.history.push("/login");
}
render() {
return (
<nav>
<span className="nav-left">
<Link className="light" to="/">
Home
</Link>
</span>
<span className="nav-right">
{this.props.authenticated ? (
<>
<Button to="/account">Account</Button>
<Button onClick={this.handleLogout} buttonStyle="danger">
Log Out
</Button>
</>
) : (
<Button to="/login">Login</Button>
)}
</span>
</nav>
);
}
}
Navigation.propTypes = {
authenticated: PropTypes.bool.isRequired,
logOut: PropTypes.func.isRequired
};
export default withRouter(Navigation);
Button
export default class Button extends Component {
state = {
classList: classNames({
button: true,
button_accent: this.props.buttonStyle === "accent",
button_danger: this.props.buttonStyle === "danger"
})
};
render() {
return (
<div className="button_container">
<div
className={classNames({
button_wrapper: true,
center: !this.props.left && !this.props.right,
left: this.props.left,
right: this.props.right
})}
>
{
this.props.to ? (
this.props.regular ? (
<a href={this.props.to} className={this.state.classList}>
{this.props.children}
</a>
) : (
<Link to={this.props.to} className={this.state.classList}>
{this.props.children}
</Link>
)
) : (
<span className={this.state.classList}>
{this.props.children}
</span>
)
}
</div>
</div>
);
}
}
Component Dev Tools
component dev tools
You forgot to pass onClick to the span that represents a button without a link
export default class Button extends Component {
state = {
classList: classNames({
button: true,
button_accent: this.props.buttonStyle === "accent",
button_danger: this.props.buttonStyle === "danger"
})
};
render() {
return (
<div className="button_container">
<div
className={classNames({
button_wrapper: true,
center: !this.props.left && !this.props.right,
left: this.props.left,
right: this.props.right
})}
>
{
this.props.to ? (
this.props.regular ? (
<a href={this.props.to} className={this.state.classList}>
{this.props.children}
</a>
) : (
<Link to={this.props.to} className={this.state.classList}>
{this.props.children}
</Link>
)
) : (
<span onClick={this.props.onClick} className={this.state.classList}>
{this.props.children}
</span>
)
}
</div>
</div>
);
}
}

Dynamically expand/collapse on click of header

I have a set of items that needs to be shown in the UI, like a header and list of items under it. There is a parent component where I am passing this data to a file that is shown below. Based on this the parent-child layout is shown. Now I need to expand/collapse based on the click of the header.
There is a class "open" and "close " that can be attached to the div. Based on it the it gets collapse/expands. The point is how do it item wise
Can someone help
import React from "react";
import Child from "./Child";
import Parent from "./Parent";
export default class Helper extends React.Component{
constructor(props: any) {
super(props);
this.state = {
parent:{},
children:{},
};
}
componentDidMount() {
this.setParentValue();
this.setChildValue();
}
render() {
const { parent, children } = this.state;
const { name } = this.props;
return (
<>
<div className="an-panel expand-panel expand-close">
<div className="an-panel-header">
<div className="title-holder">
<span className="toggle-icon far fa-plus-square" />
<span className="toggle-icon far fa-minus-square" />
<h5>{name}</h5>
</div>
<div className="action-holder">
<div className="status-holder">
<Parent
parent = {parent}
onSelect={this.handleParentClick}
/>
</div>
</div>
</div>
{children.map(({ id, name },id) => (
<div className="an-panel-body" key={id}>
<ul className="applications-list-holder">
<li>
<div className="name">{name}</div>
<div className="status">
<Child
children={children}
onSelect={this.setChildSwitchValue}
/>
</div>
</li>
</ul>
</div>
))}
</div>
</>
);
}
}
Ok let me explain it to you, here is your code
import React from "react";
import Child from "./Child";
import Parent from "./Parent";
export default class Helper extends React.Component{
constructor(props: any) {
super(props);
this.state = {
parent:{},
children:{},
navBarStatus: false,
};
}
componentDidMount() {
this.setParentValue();
this.setChildValue();
}
changeNavBar = (e, status)=>{
this.setState({navBarStatus: !status});
}
render() {
const { parent, children } = this.state;
const { name } = this.props;
return (
<>
<div className={`an-panel expand-panel ${this.state.navBarStatus ? "expand-open" : "expand-close"}`}>
<div className="an-panel-header" onClick={(e)=>this.changeNavBar(e, this.state.navBarStatus)}>
<div className="title-holder">
<span className="toggle-icon far fa-plus-square" />
<span className="toggle-icon far fa-minus-square" />
<h5>{name}</h5>
</div>
<div className="action-holder">
<div className="status-holder">
<Parent
parent = {parent}
onSelect={this.handleParentClick}
/>
</div>
</div>
</div>
{children.map(({ id, name },id) => (
<div className="an-panel-body" key={id}>
<ul className="applications-list-holder">
<li>
<div className="name">{name}</div>
<div className="status">
<ChildSetting
children={children}
onSelect={this.setChildSwitchValue}
/>
</div>
</li>
</ul>
</div>
))}
</div>
</>
);
}
}
You can see I have taken a new property in state navBarStatus. Based on navBarStatus value I am changing CSS class which will expand/close your attached div

show and hide react JS

In my react JS Code i have 2 Onclick function
class Traders extends Component {
constructor(props) {
super(props);
this.state = {
value: 10,
listVisibleList: false,
gridVisibleList: false,
listVisibleGrid: false,
gridVisibleGrid: false
};
}
componentDidMount() {
}
onClickListView() {
this.setState({
listVisibleList: !this.state.listVisibleList,
gridVisibleList: !this.state.gridVisibleList
});
}
onClickGridView(){
this.setState({
listVisibleGrid: !this.state.listVisibleGrid,
gridVisibleGrid: !this.state.gridVisibleGrid
});
}
render() {
const widthRisk = {
width: '60%'
};
return (
<div id="home">
<Col xs={12} sm={12} md={2}>
<div className="option-layout">
<ul>
<li>
<a href="#" onClick={() => this.onClickListView()}>
<img src="../../src/assets/images/list.png" className="img-respnsive" alt="list" />
</a>
</li>
<li>
<a href="#" onClick={() => this.onClickGridView()} >
<img src="../../src/assets/images/grid.png" className="img-respnsive" alt="grid" />
</a>
</li>
</ul>
</div>
</Col>
{
this.state.listVisibleList ? <ListTrader /> : <ListTrader />
}
{/* <ListTrader /> */}
{/* <GridTrader /> */}
</div>
)
}
The problem is how i want to show some specific component.
When i click onClickListView it shows <ListTrader /> Component and <GridTrader /> Component hide
When i click onClickGridView it shows <GridTrader /> Component and <ListTrader /> Component hide
I already tried to do some show and hide referring from this question
Show/Hide components in ReactJS
But the onClick from that question the function showing like toggle function. Any idea how to solve this?
Your help really appreciate. Thanks.
This is not working example but hope will give you a direction, (if I correctly understand your issue)You can hold in state string variable and based on value it has display what you want.
class Traders extends Component {
constructor(props) {
super(props);
this.state = {
....
visibleView:'list' //'grid'
};
}
componentDidMount() {}
onToggleView(view) {
this.setState({
visibleView:view
});
}
render() {
const widthRisk = {
width: '60%'
};
return (
<div id="home">
<div className="app-body" id="view">
<div className="padding font-responsive">
<Row>
..............
...............
<Col xs={12} sm={12} md={2}>
<div className="option-layout">
<ul>
<li>
<a href="#" onClick={() => this.onToggleView('list')}>
<img src="../../src/assets/images/list.png" className="img-respnsive" alt="list" />
</a>
</li>
<li>
<a href="#" onClick={() => this.onToggleView('grid')} >
<img src="../../src/assets/images/grid.png" className="img-respnsive" alt="grid" />
</a>
</li>
</ul>
</div>
</Col>
</div>
</Col>
</Row>
{
this.state.visibleView ==='list'? <ListTrader /> : <GridTrader />
}
{/* <ListTrader /> */}
{/* <GridTrader /> */}
</div>
</div>
</div>

Categories

Resources