I have two components App component and the Component2
import Component2 from './Component2';
class App extends Component {
state={data:""}
changeState = () => {
this.setState({data:`state/props of parent component
is send by onClick event to another component`});
};
render(){
return (
<div className="App">
<Component2 data={this.state.data} />
<div className="main-cointainer">
<h2>Compnent1</h2>
<button onClick={this.changeState} type="button">
Send state
</button>
</div>
</div>
);
}}
export default App;
and the Component2.js
const Component2 = (props) => {
return (
<div className="main-cointainer">
<h2>Compnent2</h2>
<p>{props.data} </p>
</div>
)
}
export default Component2;
Here I am passing the data from App component to the Component2 component . What I want is to hide
the Component2 from being rendered while passing the data from App component to the Component2 component .
I tried writing this statement
{ false && <Component2 data={this.state.data} /> }
but this did not work out .
Any suggestions to achieve this type of requirement ?
To hide Component2 (without changing its functionality), add it in a div with style = {{display: 'none'}}
App.js
import React from "react";
import "./style.css";
import Component2 from "./Component2";
export default function App() {
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
<div style={{ display: "none" }}>
<Component2 myProps="this is a example" />
</div>
</div>
);
}
Check demo : https://stackblitz.com/edit/react-bbxgcr?file=src%2FApp.js
PS : Open the console to see the props in correctly passed to Component2
you can do this with only one if. If I understood the problem correctly
const Component2 = (props) => {
if(props.data === ""){
return(<div></div>)
}
return (
<div className="main-cointainer">
<h2>Compnent2</h2>
<p>{props.data} </p>
</div>
)
}
export default Component2;
Try using negation to simply convert an empty string into a boolean value.
{ !!this.stat.data && <Component2 data={this.state.data} /> }
When the this.stat.data is an empty string or falsy value then the Component2 will not render
Or
If you don't want to show display component on the page try this
<Component2 data={this.state.data} hidden={true}/>
Component2
const Component2 =({ data, hidden }) => {
return (
<div hidden={hidden}>
<h1>Component2</h1>
</div>
);
}
Adding hidden attribute on the HTML tag will hide the element on the page.
Related
I want to pass a value which I am receiving in a function like this:
const ViewDetails = item => () => {
console.log(item);
toggleModal();
}
I want to pass the item to Modal component like open,onclose which is called in the Main function:
return (
<Layout title="Dashboard" className="container-fluid">
{<Modal open={modalStatus} onClose={() => setModalStatus(false)} />}
<div className="row">
<div className="col-sm-3">
<UserLinks />
</div>
<div className="col-sm-9">
<UserInfo />
{orders ? <PurchaseHistory /> : ""}
</div>
</div>
</Layout>
)
I am expecting to have something like this:
{<Modal open={modalStatus} onClose={() => setModalStatus(false)} ***item={item}***/>}
so that I can use the values inside item in Modal component.
I would like to add more to #GODWIN GODWIN comment in regards context API, by providing a very simple example along with the React docs about Context hook
Generally in practice people tend to wrap providers at App.js, for the sake of simplicity I am going to wrap at index.js file.
src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App'
import { ModalProvider } from './context/ModalContext'
ReactDOM.createRoot(
document.querySelector('#root')
).render(
/**
* #dev Note everything inside ModalPrivder has access
* to the values provided, such as open, setOpen
*/
<ModalProvider>
<App />
</ModalProvider>
)
src/context/ModalContext.jsx
import React, {useState, createContext, useContext } from 'react'
/**
* #dev inside your createContext object you can pass in
* default values that will be passed in value at provider
*/
export const ModalContext = createContext({
open: false
})
/**
* #dev your provider will enable you to access value all your
* children components. NOTE it will not be able to access your
* parent components.
*/
export function ModalProvider(props) {
const [open, setOpen] = useState(false)
return (
<ModalContext.Provider value={{ open, setOpen }}>
{props.children}
</ModalContext.Provider>
)
}
src/components/Modal.jsx
import { useContext } from 'react'
function Modal(props) {
const { open, setOpen } = useContext(ModalContext)
return (
<>
{ open ?
(<div>
<p>test</p>
<>{props.children}</>
<button onClick={() => setOpen(false)}>Close Close</button>
</div>) :
(<button onClick={() => setOpen(true)}>Open Modal</button>)
}
</>
)
}
export default Modal
src/App.jsx
function App(props) {
return (
<div className='App'>
<h1>Hello React.</h1>
<h2>Start editing to see some magic happen!</h2>
<Modal>
<p> You see content here</p>
</Modal>
</div>
);
}
export default App
I hope this give you a good direction on how to use React's context hook, please note that this is a very basic source code, to understand how props.children works and context hook.
You can try this:
import React from "react";
function Eexample() {
const ViewDetails = (item) => () => {
console.log(item);
toggleModal();
return (
<Modal item={item} /> //This is passing item into the Modal component
)
};
return <div>
{ViewDetails} {/*This returns the function viewDetails*/}
</div>;
}
export default Eexample;
For your Modal component:
function Modal({ item }) { // with this distructuring, you can use the item inside the Modal component
return (
<Layout title="Dashboard" className="container-fluid">
{<Modal open={modalStatus} onClose={() => setModalStatus(false)} />}
<div className="row">
<div className="col-sm-3">
<UserLinks />
</div>
<div className="col-sm-9">
<UserInfo />
{orders ? <PurchaseHistory /> : ""}
</div>
</div>
</Layout>
)
}
Or Consider using context API, it enables you to dispatch the item to your reducer.js file and pull it in your Modal component using StateProvider.js file.
You have to take state for this item. When viewDetails function triggered from inside this function you can set this state with this item afte can be pass this state as a props any component
I want to pass variable from parent component to props.children
Is posible if I access parent variable to props.children like this?
function App() {
return (
<div>
<Example>
<Example.Title>
<p>this is Title</p>
</Example.Title>
<Example.Body>
<p>this is Body</p>
<p>value from {parent}</p>
</Example.Body>
</Example>
</div>
);
}
export default App;
Parent Component
import { useState } from "react";
function Example(props) {
const [parent, setParent] = useState("parent");
return <div>{props.children}</div>;
}
Example.Title = (props) => <>{props.children}</>;
Example.Body = (props) => <>{props.children}</>;
export default Example;
You can't do it like this, because Example.Body component is already rendered when passed from App to Example.
Example must pass the "parent" prop to Example.Body
One solution is to use children as a render props (see it working there):
export default function App() {
return (
<div>
<Example>
{(parentParam) => (
<>
<Title>
<p>this is Title</p>
</Title>
<Body>
<p>this is Body</p>
<p>value from {parentParam}</p>
</Body>
</>
)}
</Example>
</div>
);
}
function Example(props) {
const [parent, setParent] = useState('parent');
return <div>{props.children(parent)}</div>;
}
I need some ideas for a folder structure/mental model in React.
I have an Item component. It stays the same.
It can be wrapped in either a Link with a URL prop or a Button with an onClick prop.
My current folder structure solution looks like this:
-Item
-wrapperComponents
-Link
-Button
Both the Link and Button components wrap around the children prop. Much like this:
react stuff ...
return(
<button onClick={props.handleOnClick}>
{props.children}
</button>
)
And this is how I call them:
<Button>
<Item />
</Button>
or
<Link>
<Item />
</Link>
I am looking for a better, more elegant solution.
I've tried sending the wrapper components to the Item but React doesn't allow to use them as a wrapper that would take children.
This is what you can do:
import "./styles.css";
import Item from "./Item";
import Wrapper1 from "./Wrapper1";
export default function App() {
return (
<div className="App">
<Item wrapper={Wrapper1} />
</div>
);
}
Item component:
const Item = ({ wrapper: Wrapper }) => {
return (
<Wrapper>
<div>This is my child</div>
</Wrapper>
);
};
export default Item;
Wrapper component:
const Wrapper1 = ({ children }) => {
return (
<div>
This is my wrapper
{children}
</div>
);
};
export default Wrapper1;
https://codesandbox.io/s/quirky-vaughan-4op2l1?file=/src/index.js
I've come up with this solution with the help you you guys
https://codesandbox.io/s/create-react-app-forked-wlmkyv?file=/src/App.js
Main component App.js:
import * as React from "react";
import Wrapper from "./components/Wrapper";
import Item from "./components/Item";
function App() {
return (
<div>
<Item wrapper={Wrapper} wrapperProps={{ color: "orange" }} />
</div>
);
}
export default App;
Item component Item.js:
import * as React from "react";
function Item(props) {
const Wrapper = props.wrapper;
const wrapperProps = props.wrapperProps;
return (
<Wrapper {...wrapperProps}>
<div>An Item</div>
</Wrapper>
);
}
export default Item;
Wrapper component Wrapper.js:
import * as React from "react";
function Wrapper(props) {
return (
<div style={{ backgroundColor: props.color || "red" }}>
{props.children}
</div>
);
}
export default Wrapper;
I have a problem with the createPortal function. When I use the "Modal", then by changing the states, the whole "Modal" component will be rendered. Can you help me how to avoid this? So when I comment out the Modal wrap in the Cart component (as I did it below), it works as I expected, but with the Modal wrap, when the states are changed, not the component will be re-rendered, but always the whole Modal Component
Here is my Modal component with createPortal function:
import React from 'react';
import { createPortal } from 'react-dom';
import { useState } from 'react';
import './modal.scss';
export default function Modal({ children, onClick }) {
const BackDrop = () => {
return <div className='backdrop' onClick={onClick}></div>;
};
const Modal = () => {
return (
<div className='modal'>
<div className='content'>{children}</div>
</div>
);
};
return (
<>
{createPortal(<BackDrop />, document.getElementById('modal'))}
{createPortal(<Modal />, document.getElementById('modal'))}
</>
);
}
The Cart component which uses the Modal component:
import React, { useEffect } from 'react';
import Modal from '../UI/Modal';
import './cart.scss';
import { useCartContext } from '../../store/CartContext';
import CartItem from './CartItem';
export default function Cart({ onHideCart }) {
const { cart, totalPrice, updateTotalPrice, addToCartOne, removeFromCartOne } = useCartContext();
useEffect(() => {
updateTotalPrice();
}, [totalPrice, cart]);
const onAddHandler = (id) => {
addToCartOne(id);
updateTotalPrice();
};
const onRemoveHandler = (id) => {
removeFromCartOne(id);
updateTotalPrice();
};
return (
// <Modal onClick={onHideCart}>
<div>
<ul className='cart-items'>
{cart.map((item, idx) => (
<CartItem
key={item.id}
name={item.name}
price={item.price}
amount={item.amount}
onAdd={onAddHandler.bind(null, item.id)}
onRemove={onRemoveHandler.bind(null, item.id)}
/>
))}
</ul>
<div className='total'>
<span>Total Amount</span>
<span>$ {totalPrice.toFixed(2)}</span>
</div>
<div className='actions'>
<button className='button--alt' onClick={onHideCart}>
Close
</button>
<button className='button'>Order</button>
</div>
</div>
// </Modal>
);
}
So by changing the amount with + and - buttons, the html element with id modal, always renders, it's flashes in the devtools... but when I comment out the Modal wrap in the Cart component, there is no flashes by modal ID. I hope it makes sense.
The problem was with the two custom element inside of the Cart element. When I return
createPortal(
<>
<div className='backdrop' onClick={onClick}></div>
<div className='modal'>
<div className='content'>{children}</div>
</div>
</>,
document.getElementById('modal')
)
instead of this:
<>
{createPortal(<BackDrop />, document.getElementById('modal'))}
{createPortal(<Modal />, document.getElementById('modal'))}
</>
Then there is no problem with rendering.
I have two components:
Parent.js
import { useState } from "react";
function Parent() {
const [showHideContent, setShowHideContent] = useState("none");
return (
<div style={{ display: showHideContent }}>
Some content here...
</div>
);
}
Child.js
function Child() {
return (
<button>
Show/Hide Content
</button>
);
}
I want to pass two values none and block (one value at a time) through setShowHideContent of Parent component using Show/Hide Content button of Child component.
How to do this?
NOTE: These two components are saved in the same folder but in two different files.
These two component are rendered by App.js.
<Route path="/content">
<Menu /> {/* rendering in LEFT */}
<div className="content-flexbox">
<Parent /> {/* rendering in CENTER */}
<Child /> {/* rendering in RIGHT */}
</div>
<Footer /> {/* rendering in BOTTOM */}
</Route>
It seems like you want the Child component to simply toggle the display value of some content in the Parent component.
As you've defined them though they are not parent-child, but rather they are siblings. As such if they need to share state/behavior, then the solution is to lift state up to a common ancestor, App in this case.
<Route path="/content">
<Menu />
<div className="content-flexbox">
<Parent /> // <-- siblings
<Child /> // <-- siblings
</div>
<Footer />
</Route>
See: Lifting State Up
Move the showHideContent state and updater into the parent App component, pass down the showHideContent state to Parent and the toggleVisibility callback to the Child.
function Parent({ showHideContent }) {
return <div style={{ display: showHideContent }}>Some content here...</div>;
}
function Child({ onClick }) {
return (
<button type="button" onClick={onClick}>
Show/Hide Content
</button>
);
}
export default function App() {
const [showHideContent, setShowHideContent] = React.useState("none");
const toggleVisibility = () =>
setShowHideContent((value) => (value === "none" ? "block" : "none"));
return (
...
<Route path="/content">
<Menu />
<div className="content-flexbox">
<Parent showHideContent={showHideContent} />
<Child onClick={toggleVisibility} />
</div>
<Footer />
</Route>
...
);
}
You can simply pass values as prop to child component.
Call your child component inside parent component and pass value
import { useState } from "react";
import Child from "//your path";
function Parent() {
const [showHideContent, setShowHideContent] = useState("none");
return (
<div style={{ display: showHideContent }}>
<Child showHide={showHideContent}/>
</div>
);
}
Now in your child component
function Child({showHide}) {
return (
<button>
{showHide}
Show/Hide Content
</button>
);
}
Include your Child component in your Parent component and pass hooks as props
import { useState } from "react";
// import the child component
function Parent() {
const [showHideContent, setShowHideContent] = useState("none");
return (
<div style={{ display: showHideContent }}>
Some content here...
<Child showHideContent={showHideContent}/>
</div>
);
}
And use them in your child components
function Child({showHideContent}) {
return (
<button>
{showHideContent}
Show/Hide Content
</button>
);
}
Another way of doing the same is by using Context API:
https://reactjs.org/docs/hooks-reference.html#usecontext
You can pass an inline function as prop from parent to child like this.
function Parent(){
const [showHideContent, setShowHideContent] = useState("none");
return (
<Child onButtonClicked={(toValue) => setShowHideContent(toValue)} />
)
}
function Child({ onButtonClicked }) {
return (
<button onClick={onButtonClicked} >
Show/Hide Content
</button>
);
}
Pass down exact state modifier with same name
function Parent(){
const [showHideContent, setShowHideContent] = useState("none");
return (
<Child setShowHideContent={setShowHideContent} />
)
}
//or with prop speading
function Parent(){
const [showHideContent, setShowHideContent] = useState("none");
return (
<Child {...{setShowHideContent}} />
)
}