get data from child components to parents components in react js - javascript

I want to get value from child components to parent components.
Here is my code.
//this is child component
import { React } from "react";
const Tab = () => {
const changeTab = (index) => {
console.log(index);
};
return (
<>
<div className="flex gap-10">
<button
onClick={() => changeTab(1)}
className="bg-gray-700 p-2 text-white"
>
btn1
</button>
</div>
</>
);
};
export default Tab;
//this is parent component
import React from "react";
import Nav1 from "./Components/Navbar/Nav1";
import Tab from "./Tab";
const App = () => {
return (
<>
<Tab />
<div>
<Nav1 />
</div>
</>
);
};
export default App;
I want to log the value of the index in a parent component that was coming from a child.

define changeTab in parent component and pass it through props to Tab.
parent:
import React from "react";
import Nav1 from "./Components/Navbar/Nav1";
import Tab from "./Tab";
const App = () => {
const changeTab = (index) => {
console.log(index);
};
return (
<>
<Tab changeTab={changeTab}/>
<div>
<Nav1 />
</div>
</>
);
};
export default App;
and child component:
import { React } from "react";
const Tab = ({changeTab}) => {
const onChangeTab = (index) => {
changeTab(index);
// other stuff
};
return (
<>
<div className="flex gap-10">
<button
onClick={() => onChangeTab(1)}
className="bg-gray-700 p-2 text-white"
>
btn1
</button>
</div>
</>
);
};
export default Tab;

You can use context API:
https://reactjs.org/docs/context.html
Or you can transfer changeTab function to parent component and pas it as prop

Your problem is common(with most people). And the solution is to lift the state up(React docs).
Which basically means, you'll have to maintain the state in the parent component and pass the value and method to the child component.
// App.jsx
import React from "react";
import Nav1 from "./Components/Navbar/Nav1";
import Tab from "./Tab";
const App = () => {
const changeTab = (index) => {
console.log(index);
};
return (
<>
<Tab handleTabChange={changeTab}/>
<div>
<Nav1 />
</div>
</>
);
};
export default App;
import { React } from "react";
const Tab = (props) => {
const changeTab = (index) => {
props.handleTabChange(index);
};
return (
<>
<div className="flex gap-10">
<button
onClick={() => changeTab(1)}
className="bg-gray-700 p-2 text-white"
>
btn1
</button>
</div>
</>
);
};
export default Tab;

Related

How to fix saving duplicate items in my Reactjs todo list app?

Summary
I am trying to create todo list in ReactJS. I expect only one list item to be added per action.
Actual Behavior
When I am adding to my todolist item, it adds the item twice.
App.js
import Header from "./components/Header";
import List from "./components/List";
import {useState} from "react";
function App() {
const [items, setItem] = useState(function () {
const storageItemsString = localStorage.getItem('items');
if (storageItemsString) {
return JSON.parse(storageItemsString);
} else {
return [];
}
}
);
function addItem(fieldValue) {
setItem( (prevItems) => {
const newItems = prevItems.concat(fieldValue);
localStorage.setItem('items', JSON.stringify(newItems));
return newItems;
});
}
return (
<div className="App">
<div className="wrapper">
<Header/>
<List addItem={addItem} items={items}/>
</div>
</div>
);
}
export default App;
List jsx
import React, {useEffect} from 'react';
import {Button, TextField} from "#mui/material";
import ListItem from "./listItem";
function List(props) {
useEffect(() => {
const textField = document.querySelector('#list-field');
const button = document.querySelector('.button');
button.addEventListener('click', function(e) {
if (textField.value !== '') {
props.addItem(textField.value);
}
});
}, [props]);
return (
<div className="list-section">
<form action="#" className="list-form">
<TextField variant="outlined" label="Enter something" id="list-field" multiline
rows={4}/>
<Button variant="contained" className="button">Add item</Button>
</form>
<div className="list-items">
{props.items.map( (item) => <ListItem text={item} />)}
</div>
</div>
);
}
export default List;
ListItem jsx
import React from 'react';
import DeleteIcon from '#mui/icons-material/Delete';
function ListItem(props) {
return (
<div className="list-item">
<div className="item-content">{props.text}</div>
<span className="delete-icon">
<DeleteIcon />
</span>
</div>
);
}
export default ListItem;

Parent action inside child component react

I am having two components, App and a panel. On button clcik, I add panel to the screen and all the actions corresponding actions inside of the panel is handled in the Panel component ( Actions are expand, collapse and close). Can I execute onClose method inside of the Panel component instead in App component. I am having a check of if cardBody is present in the parent component. For removing that card im setting its body to null inside App component. How can I do the same in Panel component.
https://codesandbox.io/s/basic-demo-card-6ywop7?file=/src/Panel.jsx:0-1022
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Panel from "./Panel";
import "./styles.css";
function App() {
const [card, setCard] = useState({
cardId: "",
cardBody: null
});
const handleClick = (cardId, cardBody) => {
setCard({ cardId, cardBody });
};
const { cardId, cardBody } = card;
return (
<>
<div className="main">
<button onClick={() => handleClick("Panel 1", <h1>h1</h1>)}>
Add Panel 1
</button>
<button onClick={() => handleClick("Panel 2", <div>div</div>)}>
Add Panel 2
</button>
</div>
{cardBody && (
<div className="cards-container">
<Panel
key={cardId}
cardId={cardId}
cardBody={cardBody}
onClose={() =>
setCard({
cardId: "",
cardBody: null
})
}
/>
</div>
)}
</>
);
}
import React, { useState, useCallback } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {
faSquareMinus,
faRectangleXmark
} from "#fortawesome/free-solid-svg-icons";
function Panel(props) {
const [isMinimized, setIsMinimized] = useState(false);
const { cardId, cardBody, onClose } = props;
const onMaximize = useCallback(() => {
setIsMinimized(!isMinimized);
}, [isMinimized]);
return (
<>
<div className={isMinimized ? "card-min" : "card"}>
<div className="card-actions">
<span onClick={onMaximize}>{cardId}</span>
{!isMinimized && (
<FontAwesomeIcon
icon={faSquareMinus}
onClick={() => {
setIsMinimized(true);
}}
/>
)}
<FontAwesomeIcon icon={faRectangleXmark} onClick={onClose} />
</div>
<div className="card-body">{cardBody}</div>
</div>
</>
);
}
export default Panel;
One workaround is to control the show/hide by display: 'none'/'block' inside the Panel component.
Panel component
import React, { useImperativeHandle, useState, useCallback } from
"react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {
faSquareMinus,
faRectangleXmark
} from "#fortawesome/free-solid-svg-icons";
function Panel(props) {
const [isMinimized, setIsMinimized] = useState(false);
const { cardId, cardBody, setCard } = props;
const onMaximize = useCallback(() => {
setIsMinimized(!isMinimized);
}, [isMinimized]);
return (
<>
<div
style={{ display: cardBody ? "block" : "none" }}
className={isMinimized ? "card-min" : "card"}
>
<div className="card-actions">
<span onClick={onMaximize}>{cardId}</span>
{!isMinimized && (
<FontAwesomeIcon
icon={faSquareMinus}
onClick={() => {
setIsMinimized(true);
}}
/>
)}
<FontAwesomeIcon
icon={faRectangleXmark}
onClick={() =>
setCard({
cardId: "",
cardBody: null
})
}
/>
</div>
<div className="card-body">{cardBody}</div>
</div>
</>
);
}
export default Panel;
App.js
import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";
import Panel from "./Panel";
import "./styles.css";
function App() {
const [card, setCard] = useState({
cardId: "",
cardBody: null
});
const handleClick = (cardId, cardBody) => {
setCard({ cardId, cardBody });
};
const { cardId, cardBody } = card;
return (
<>
<div className="main">
<button onClick={() => handleClick("Panel 1", <h1>h1</h1>)}>
Add Panel 1
</button>
<button onClick={() => handleClick("Panel 2", <div>div</div>)}>
Add Panel 2
</button>
</div>
<div className="cards-container">
<Panel
key={cardId}
cardId={cardId}
cardBody={cardBody}
setCard={setCard}
/>
</div>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
This is working example
Also, you can completely conditionally show and hide the inner content of the panel but you need some modifications in the style by moving the "card-min" and "card" class to an inner div instead of the root element on the component.

how to change child component state in reactjs

I came across the following problem: I need to use another component's function inside a component:
header.js
export default function Header() {
const [showModalLogin ,setShowModalLogin] = useState(false);
return(
<>
<span>Hi, <Link onClick={() =>{ setShowModalLogin(true)}}>login</Link> </span>
{showModalLogin ? <LoginModal setShowModalLogin={setShowModalLogin}/> : ''}
</>
);
}
home.js
import Header from '../../components/header/header';
export default function Home() {
return (
<>
<Header />
<Link onClick={() =>{ setShowModalLogin(true)}}>open login</Link>
</>
}
How do I do in home.js to call the setShowModalLogin function that is in header.js ? I'm trying to use the context api too, but I still can't solve it.
You can just place useState in Header component and pass setShowModalLogin as props:
import Header from '../../components/header/header';
export default function Home() {
const [isShowModalLogin ,setShowModalLogin] = useState(false);
return (
<>
<Header isShowModalLogin={isShowModalLogin} setShowModalLogin={setShowModalLogin} />
<Link onClick={() => setShowModalLogin(true)}>open login</Link>
</>
}
export default function Header({ isShowModalLogin, setShowModalLogin }) {
return(
<>
<span>Hi, <Link onClick={() => setShowModalLogin(true)}>login</Link> </span>
{isShowModalLogin ? <LoginModal setShowModalLogin={setShowModalLogin}/> : ''}
</>
);
}
Then you can do it in this way:
Create Context
Save useState inside
Use it everywhere you need
export const YourContext = createContext();
export const YourProvider = ({ children }) => {
const [isShowModalLogin, setShowModalLogin] = useState(false);
const value = {
isShowModalLogin,
setShowModalLogin
};
return <YourContext.Provider value={value}>{children}</YourContext.Provider>;
}
// App.js
const App = () => {
return (
<YourProvider>
<AppContent />
</YourProvider>
)
}
So now you can use it like here:
import Header from '../../components/header/header';
export default function Home() {
const { isShowModalLogin, setShowModalLogin } = useContext(YourContext);
return (
<>
<Header isShowModalLogin={isShowModalLogin} setShowModalLogin={setShowModalLogin} />
<Link onClick={() => setShowModalLogin(true)}>open login</Link>
</>
}

My modal doesn't show when I click a button

Here I have my modal component. I am making an app that I want a button to open this modal that I use in multiple places like opening a preview or deleting options.
import React from 'react';
import ReactDOM from 'react-dom';
import { CSSTransition } from 'react-transition-group';
import Backdrop from '../Backdrop/Backdrop';
import '../Modal/Modal.css';
const ModalOverlay = (props) => {
const content = (
<div className={`modal ${props.className}`} style={props.style}>
<header className={`modal__header ${props.headerClass}`}>
<h2>{props.header}</h2>
</header>
<form
onSubmit={
props.onSubmit ? props.onSubmit : (event) => event.preventDefault()
}
>
<div className={`modal__content ${props.contentClass}`}>
{props.children}
</div>
<footer className={`modal__footer ${props.footerClass}`}>
{props.footer}
</footer>
</form>
</div>
);
return ReactDOM.createPortal(content, document.getElementById('modal-hook'));
};
const Modal = (props) => {
return (
<React.Fragment>
{props.show && <Backdrop onClick={props.onCancel} />}
<CSSTransition
in={props.show}
mountOnEnter
unmountOnExit
timeout={200}
classNames="modal"
>
<ModalOverlay {...props} />
</CSSTransition>
</React.Fragment>
);
};
export default Modal;
And here I use this modal for showing up deleting options.
const DocumentItem = (props) => {
const [showConfirmModal, setShowConfirmModal] = useState(false);
const showDeleteWarningHandler = () => {
setShowConfirmModal(true);
};
const calcelDeleteHandler = () => {
setShowConfirmModal(false);
};
const confirmDeleteHandler = () => {
setShowConfirmModal(false);
console.log('Delete!');
};
return (
<React.Fragment>
<Modal
show={showConfirmModal}
onCancel={calcelDeleteHandler}
header="Are you sure?"
footerClass="document-item__modal-actions"
footer={
<React.Fragment>
<Button inverse onClick={calcelDeleteHandler}>
CANCEL
</Button>
<Button danger onClick={confirmDeleteHandler}>
DELETE
</Button>
</React.Fragment>
}
>
<p>
Do you want to proceed and delete this document? Please note that it
can't be undone thereafter.
</p>
</Modal>
</React.Fragment>
);
};
I don't understand why my screen goes all black, transparent but my modal doesn't show.
How can I fix this problem?

How to avoid unexpected rendering while using React Context?

I have two functional component under my provider,
SubApp1 and SubApp2 and here when I am increasing counter1 in SubApp1 the SubApp2 also is rendering, even when it is not need to be re-rendered.
And when I am increasing counter2 in SubApp2 the SubApp1 also is rendering.
I know this happens regally, but How can avoid this situation ?
App.js:
import React, {useContext, useState, memo} from "react";
import "./styles.css";
export const MainContext = React.createContext();
export const MainProvider = ({children})=> {
const [counter1, setCounter1] = useState(0);
const [counter2, setCounter2] = useState(0);
return (
<MainContext.Provider value={{
counter1, setCounter1,
counter2, setCounter2,
}}>
{children}
</MainContext.Provider>
);
}
export const SubApp1 = memo(()=> {
const {counter1, setCounter1} = useContext(MainContext);
console.log('Counter 1: ', counter1);
return (
<div className="App">
<button onClick={()=> {
setCounter1(counter1+1);
}}>
Increase Count 1
</button>
</div>
);
});
export const SubApp2 = memo(()=> {
const {counter2, setCounter2} = useContext(MainContext);
console.log('counter2: ', counter2);
return (
<div className="App">
<button onClick={()=> {
setCounter2(counter2+1);
}}>
Increase Count 2
</button>
</div>
);
});
export default function App ({navigation}){
console.log('App Is rendering...');
return (
<div className="App">
<button onClick={()=> {
navigation.navigate('SubApp1');
}}>
navigate to SubApp1
</button>
<button onClick={()=> {
navigation.navigate('SubApp2');
}}>
navigate to SubApp2
</button>
</div>
);
}
index.js:
import React from "react";
import ReactDOM from "react-dom";
import App, {MainProvider} from "./App";
const MainApp = ()=> (
<MainProvider>
<App />
</MainProvider>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<MainApp />, rootElement);
You should pass the counter to the SubApps as props. Then memo will take care that only the component with changing props will be rerendered.
Something like this:
export const Wrapper1 = ()=> {
const {counter1, setCounter1} = useContext(MainContext);
return (
<SubApp1 {...{counter1, setCounter1}} />
);
};
export const SubApp1 = memo(({counter1, setCounter1})=> {
console.log('Counter 1: ', counter1);
return (
<div className="App">
<button onClick={()=> {
setCounter1(counter1+1);
}}>
Increase Count 1
</button>
</div>
);
});
export const SubApp2 = memo(({counter2, setCounter2})=> {
console.log('counter2: ', counter2);
return (
<div className="App">
<button onClick={()=> {
setCounter2(counter2+1);
}}>
Increase Count 2
</button>
</div>
);
});
export default function App (){
const {counter2, setCounter2} = useContext(MainContext);
console.log('App Is rendering...');
return (
<div className="App">
<Wrapper1/>
<SubApp2 {...{counter2, setCounter2}} />
</div>
);
}
Codesandbox link is not right...
I follow the tip of Peter Ambruzs, but i have a problem if i pass counter1 as a param. The component keep rerendering.
But, if i pass just setCounter1 function, its works fine.
Below, my example using typescript.
const Campaigns = (): JSX.Element => {
const { setAlert } = useContext(AlertContext);
return <InnerCampaign {...{ setAlert }} />;
};
const InnerCampaign = memo(
({ setAlert }: any): JSX.Element => {...},)
export default Campaigns;

Categories

Resources