I have a two child components and I want a button in one of them to toggle (on/off) the html in another. I have watched tutorials on lifting state up, and communication between components but most of it has to do with moving data. I want to manipulate a component from another. I would like the achieve this using useState, and without useContext if possible. Thanks!
THE PARENT
import React from "react";
function App() {
return (
<div>
<AppProvider>
<ComponentOne/>
<Slideshow />
<ComponentTwo/ >
</AppProvider>
</div>
);
}
CHILD 1
export default function ComponentOne() {
return (
<div>
<button>The button that toggles</button>
</div>
);
}
CHILD 2
export default function ComponentTwo() {
return (
<div>
<div>The content that hides/shows</div>
</div>
);
}
You need to use state to toggle the values. Pass the state to the ComponentTwo as props and pass the state updater function as the callback function to the ComponentOne as a prop.
import React , { useState, useCallback } from 'react';
function ComponentOne({ onToggle}) {
return (
<div>
<button onClick={onToggle}>The button that toggles</button>
</div>
);
}
function ComponentTwo({show}) {
return (
<div>
{show && <div>The content that hides/shows</div>}
</div>
);
}
export default function App() {
const [show, setShow ] = useState(true);
const handleToggle = useCallback(() => setShow(prevShow => !prevShow),[])
return (
<div className="App">
<ComponentOne onToggle={handleToggle} />
<ComponentTwo show={show} />
</div>
);
}
Refer
useState
useCallback
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 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 am not very experienced with React but I have a very simple Setup.
export default function App() {
const [title, setTitle] = useState("still-empty");
const myFunction = title => {
setTitle(title);
};
return (
<div className="App">
<ComponentA myFunction={myFunction} />
<br />
<br />
<ComponentB title={title} />
</div>
);
}
const ComponentA = ({ myFunction }) => {
console.log("Rendering Component A");
return (
<div onClick={() => myFunction(Math.random() * 1000)}> Component A </div>
);
};
export default ComponentA;
const ComponentB = ({ title }) => {
return <div> Title : {title}</div>;
};
export default ComponentB;
Here is a sandbox to test this: https://codesandbox.io/s/musing-cookies-g7szr
See that if you click on "ComponentA", that exact ComponentA gets rerendered (you can see it in console) although no props are changed on this component. This is a simplified example of my real use case. In my real use case, ComponentA is a map where a lot of stuff (zoom, center)
will be reset. I want to prevent these resets and also the 1 second it takes for rerendering. Therefor I present this simplified example.
So how do I pass an information from ComponentA to ComponentB, without rerendering ComponentA itself? Thanks for helping out here.
use useCallback in Parent so that the function is not created again and again but only on initial render.
use React.memo so that when no props are changed the component wont re-render.
App
export default function App() {
const [title, setTitle] = useState("still-empty");
const myFunction = useCallback(title => {
setTitle(title);
}, []);
return (
<div className="App">
<ComponentA myFunction={myFunction} />
<br />
<br />
<ComponentB title={title} />
</div>
);
}
ComponentA
import React, { memo } from "react";
const ComponentA = ({ myFunction }) => {
console.log("Rendering Component A");
return (
<div onClick={() => myFunction(Math.random() * 1000)}> Component A </div>
);
};
export default memo(ComponentA);
Working demo is here:
https://codesandbox.io/s/affectionate-boyd-v7g2t?file=/src/App.js
-I am using function component.
-for now I am using 3 components here, from that One is parent component and another 2 are child components.
-I need to access one child component methods or state to another child methods. I already done with class components with CreateRef but for now I need to use with function components but I am getting Null inside 'ref.current'.
export function SideBySideList(props) {
const ref = React.createRef();
//this is call inside ListPage after sucess
function updateRightList(id) {
ref.current.state.actualSearchedModel.Id = id
ref.current.fetchDataAndUpdate();
}
function itemClicked(id) {
updateRightList(id);
}
return (
<>
<div className="col-12 no-padding">
<div className={props.leftListLayoutClass}>
<ListPage
updateRightList={updateRightList}
/>
</div>
<div className={props.rightListLayoutClass}>
<ListPage
ref={ref}
/>
</div>
</div>
<>
);
}
According to the official documentation:
You may not use the ref attribute on function components because they
don’t have instances
So if your ListPage is functional component, you have to convert it to the class component. Or your ref must refer to the DOM element inside of ListPage.
function ListPage ({ref}) {
return <div ref={ref}>Hello!</div>
}
UPDATED:
function ParentComponent () {
const [state, setState] = React.useState(null);
const onChildMount = React.useCallback((dataFromChild) => {
setState(dataFromChild);
});
return (
<div>
<pre>{JSON.stringify(state, null, 2)}</pre>
<ChildComponent onMount={onChildMount} />
</div>
)
}
function ChildComponent (props) {
const thisShouldBePassedToTheParent = "from child with love";
React.useEffect(() => {
props.onMount(thisShouldBePassedToTheParent);
}, []);
return (
<div>child component</div>
)
}
ReactDOM.render(<ParentComponent />, document.querySelector("#root"));
<script src="https://unpkg.com/react#16.9.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16.9.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
With functional components you can use refs like this:
// Import our hooks
import React, { useRef, useEffect } from 'react';
// create our ref
const myInput = useRef();
// This is equivalent to our componentDidMount, this will focus
useEffect(() => myInput.current && myInput.current.focus());
// Parse our ref to our textField
<Textfield inputRef={myInput} />
Here you can read docs https://reactjs.org/docs/hooks-reference.html#useref
Also you may use refs like this directly:
<TextField inputRef={input => input && input.focus()} />
You can read full Article here: https://medium.com/javascript-in-plain-english/react-refs-both-class-and-functional-components-76b7bce487b8
If someone looking for solution where Parent is class component and Child is functional component, and want to get data from child (state, function)
Class component:
class Parent extends React.Component {
constructor(){
this.setReferenceToElement = element => {
this.fromChild = element;
}
}
handleClick(){
console.log(this.fromChild());
}
render(){
return (
<div>
<Child setRef={this.setReferenceToElement} />
<button onClick={handleClick}> Get state from child </button>
</div>
)
}
}
Functional component:
function Child(props){
// ... it has some state
props.setRef(state => state);
return <div> Test </div>
}