How to pass state properties from component to component - reactjs

I am currently learning React and I am trying to create a basic todo list app. I am facing an issue in the understanding of how passing data from component to component.
I need that when I add a task in the modal of my home component it gets added in the "pub" state of my public task component in order for the task to be rendered.
I joined the code of both components,
Hope someone can help me :)
function PublicTask (){
const [pub,setPub] = useState([{id: 1, value : "test"},{id: 2, value : "test2"}]);
function ToDoPublicItem() {
const pubT = pub.map(value =>{
return(
<div className= 'pubTask-item'>
<li>{value.value}</li>
</div>
)
});
return(
<div>
<div>
{pubT}
</div>
</div>
);
}
return(
<div className= 'item-container'>
<h2 style={{color:'white'}}>Public Tasks</h2>
<ToDoPublicItem/>
</div>
);
}
export default PublicTask;
function Home() {
const [show,setShow] = useState(false);
const [pubTask,setPubTask] = useState([]);
function openModal() {
setShow(true);
}
function Modal(){
const[textTodo, setTextTodo] = useState('')
const addItem = () => {
const itemTopush = textTodo;
pubTask.push(itemTopush);
}
return(
<div className='modal'>
<div className = 'modal-title'>
<h2>ADD A TODO</h2>
<hr></hr>
</div>
<div className= 'modal-body'>
<input type='text' onChange = {(e) => setTextTodo(e.target.value)}/>
<input type="checkbox" id="pub" value ='public'/>
<label Htmlfor="pub">Public</label>
<input type="checkbox" id="priv" value= 'private '/>
<label Htmlfor="riv">Private</label>
<hr></hr>
<Button id='button-add' size='large' style={{backgroundColor : 'white'}} onClick={()=> addItem()}>ADD</Button>
<hr></hr>
<Button id='button-close' size='large' style={{backgroundColor : '#af4c4c'}} onClick= {()=> setShow(false)} >CLOSE</Button>
</div>
</div>
)
}
return(
<div>
<h1 style={{textAlign:'center'}}>You are logged in !</h1>
<div>
<button id='button-logout' onClick = {() => firebaseApp.auth().signOut()}>Logout</button>
</div>
<div>
<Fab color="primary" aria-label="add" size = 'large' onClick = {() => openModal()}>
<Add/>
</Fab>
{show ? <Modal/> : <div></div>}
</div>
<div>
<Router>
<div className='pub-container'>
<Link to='/publicTasks'>Public Tasks </Link>
</div>
<div className='ongo-container'>
<Link to='/onGoingTasks'>On Going Tasks </Link>
</div>
<div className='finish-container'>
<Link to='/finishedTasks'>Finished Tasks </Link>
</div>
<Route path='/publicTasks' component = {PublicTask}/>
<Route path='/onGoingTasks' component = {OngoingTask}/>
<Route path='/finishedTasks' component = {FinishedTask}/>
</Router>
</div>
</div>
);
}
export default Home;

You can share data between react components like this:
const [value, setValue] = useState("test"); // data that you want to share
return (
<Parent data={value}> // pass data to parent in your child component
);
<h1>{this.props.data}</h1> // do something with the data in the parent component

Related

onClick load react component in the same place

I have a panel with 3 buttons, i want to make onclick on every button, a different component will appear in the same place. How can this logic be done?
<AddNotification />
<EditNotification />
<DeleteNotification />
const AdminPanel = () => {
return (
<Card className={classes.input}>
<div><h1>Notification Panel</h1></div>
<form>
<div className="form-group">
<Button type="submit">Add Notification</Button>
</div>
<div className="form-group">
<Button type="submit">Edit Notification</Button>
</div>
<div className="form-group">
<Button type="submit">Delete Notification</Button>
</div>
</form>
</Card>
)
}
#MLDC No i don't have another divs, i want to replace the buttons with the crossponding component. For example: onclick on Add, then Add component will appears instead of the buttons.
In that case, create a boolean state for every Panel that you have (I created 3 so that you could open the panels simultaneously),
const [isAddPanelOpen, setIsAddPanelOpen] = useState(false);
const [isEditPanelOpen, setIsEditPanelOpen] = useState(false);
const [isDeletePanelOpen, setIsDeletePanelOpen] = useState(false);
Next, apply this to every button
<Button onClick={setIsAddPanelOpen(prevState=>!prevState)}>Add Notification</Button>
<Button onClick={setIsEditPanelOpen(prevState=>!prevState)}>Edit Notification</Button>
<Button onClick={setIsDeletePanelOpen(prevState=>!prevState)}>Delete Notification</Button>
Lastly, Refactor your html to
<div className="form-group">
{isAddPanelOpen ? <AddNotification/> : <Button type="submit">Add Notification</Button>}
</div>
<div className="form-group">
{isEditPanelOpen ? <EditNotification/> : <Button type="submit">Edit Notification</Button>}
</div>
<div className="form-group">
{isDeletePanelOpen ? <DeleteNotification/> :<Button type="submit">Delete Notification</Button>}
</div>
Try this if you want to display one component at a time and hide the others when you click a button
const AdminPanel = () => {
const [componentToDisplay, setComponentToDisplay] = useState("")
return (
<>
<Card className={classes.input}>
<div><h1>Notification Panel</h1></div>
<form>
<div className="form-group">
{componentToDisplay !== "add ? (
<Button type="submit" onCLick={() => setComponentTodisplay("add")}>Add Notification</Button>)
:(<AddNotification />)}
</div>
<div className="form-group">
{componentToDisplay !== "edit ? (
<Button type="submit" onCLick={() => setComponentTodisplay("edit")}>Edit Notification</Button>)
:(<EditNotification />)}
</div>
<div className="form-group">
{componentToDisplay !== "delete ? (
<Button type="submit" onCLick={() => setComponentTodisplay("delete")}>Delete Notification</Button>)
:(<DeleteNotification />)}
</div>
</form>
</Card>
</>
)
}
Or if you want to be able to replace every buttons, use this logic with one state per button
const AdminPanel = () => {
const [addNotif, setAddNotif] = useState(false)
const [editNotif, setEditNotif] = useState(false)
const [deleteNotif, setDeleteNotif] = useState(false)
return (
<>
<Card className={classes.input}>
<div><h1>Notification Panel</h1></div>
<form>
<div className={`form-group ${editNotif || deleteNotif ? "display: none" : "display: flex"}`}>
{!addNotif ? (
<Button type="submit" onCLick={() => setAddNotif(true)}>Add Notification</Button>)
:(<AddNotification setAddNotif={setAddNotif} />)}
</div>
<div className={`form-group ${addNotif || deleteNotif ? "display: none" : "display: flex"}`}>
{!editNotif ? (
<Button type="submit" onCLick={() => setEditNotif(true)}>Edit Notification</Button>)
:(<EditNotification setEditNotif={setEditNotif} />)}
</div>
<div className={`form-group ${addNotif || editNotif ? "display: none" : "display: flex"}`}>
{!deleteNotif ? (
<Button type="submit" onCLick={() => setDeleteNotif(true)}>Delete Notification</Button>)
:(<DeleteNotification setDeleteNotif={setDeleteNotif} />)}
</div>
</form>
</Card>
</>
)
}
Then in your component
const AddNotification = ({setAddNotif}) => {
...
return (
...
<button onCLick={() => setAddNotif(false)}>back</button>
...
)
}
Same logic for the other components
To achieve this logic you need to manage which component is displayed using a state.
This means:
Attribute an arbitrary id to each component.
Store the id of the active component in a useState hook.
Use conditional rendering to display the component that match the current state.
Update the state to the corresponding Id when clicking on each button.
A small example
const [activePanel, setActivePanel] = React.useState(0)
let currentPanel = <Panel0 />
switch(activePanel){
case 0:
currentPanel = <PanelO />
case 1:
currentPanel = <Panel1 />
// Continue as needed
}
return (
<div>
<CurrentPanel/>
<button onClick={() => setActivePanel (0)}> Panel 0 </button>
<button onClick={() => setActivePanel (1)}> Panel 1 </button>
// And so on
</div>
)
You can further refine this by extracting the switch statement into its own component that takes the activePanel as a prop.

Grabbing the Value of a Dynamically Created Object

I am dynamically creating buttons from an API call that looks like this:
My goal is that when a button is clicked the inner text will display in the search bar above.
below is the code for this auto complete component:
const Autocomplete = (props) =>
{
const btnSearch = (e) => {
console.log(props.suggestions)
}
return(
<>
{props.suggestions.map((e) => (
<button className={style.btn} onClick={btnSearch} key={e}>{e}</button>
))}
</>
);
}
export default Autocomplete;
The Autocomplete component is then being placed in a div as seen here:
return (
<>
<div className={style.container}>
<div className={style.title_hold}>
<h1>turn one</h1>
<h2>a search engine and game companion for Magic: The Gathering</h2>
</div>
<input className={style.search} type='text' placeholder='search for cards here...' value={search} onChange={handleInputChange} onKeyPress={handleSubmit}></input>
<div className={style.btn_wrap}>
<Autocomplete suggestions={results} />
</div>
<div className={style.data_wrap} id='user_display'>
<div className={style.img_wrap}>
{photos}
</div>
<div className={style.display_info}>
<h2>{card.name}</h2>
<h2>{card.type_line}</h2>
<h2>{card.oracle_text}</h2>
</div>
</div>
</div>
</>
)
Any help would be appreciated.
You can create a state variable in your parent component and then pass a function to the Autocomplete's button for the onClick which will then update the state in the parent. Something like this:
const Autocomplete = (props) => {
return(
<>
{props.suggestions.map((e) => (
<button className={style.btn} onClick={() => props.setSearch(e)} key={e}>{e}</button>
))}
</>
);
}
export default Autocomplete;
Your parent component:
import React from 'react'
const ParentComponent = (props) => {
const [searchText, setSearchText] = React.useState("")
const handleClick = (textFromButtonClick) => {
setSearchText(textFromButtonClick)
}
return (
<>
<div className={style.container}>
<div className={style.title_hold}>
<h1>turn one</h1>
<h2>a search engine and game companion for Magic: The Gathering</h2>
</div>
<input className={style.search} type='text' placeholder='search for cards here...' value={searchText} onChange={handleInputChange} onKeyPress={handleSubmit}></input>
<div className={style.btn_wrap}>
<Autocomplete setSearch={handleClick} suggestions={results} />
</div>
<div className={style.data_wrap} id='user_display'>
<div className={style.img_wrap}>
{photos}
</div>
<div className={style.display_info}>
<h2>{card.name}</h2>
<h2>{card.type_line}</h2>
<h2>{card.oracle_text}</h2>
</div>
</div>
</div>
</>
)
}
export default ParentComponent;
I took over your input value in the parent component, but without seeing any of your other code, I have no idea how you're managing that but you can likely merge it into this workflow to wire it up.

Share State to other component | pass data to other component

hello I'm currently learning about React, and I'm confused about how to pass data or state to another component
i have Search Component like this
function Search(props) {
const [ query, setQuery ] = useState("")
const [ movie, setMovie ] = useState({})
function searchHandler() {
axios.get(`${api.url}SearchMovie/${api.key}/${query}`)
.then(res => {
setMovie(res.data.results)
}).catch(err => {
console.log(err)
})
}
return (
<div className="search-input">
<div class="input-group input-custom mb-3">
<input
type="text"
class="form-control"
placeholder="Search Movie"
onChange={e => setQuery(e.target.value)}
value={query}
/>
<button
class="btn btn-outline-primary"
onClick={searchHandler}
>
Search
</button>
</div>
</div>
);
}
export default Search;
and also MainPage Component like this
function MainPage() {
return (
<div>
<Navbar />
<div className="container">
<Search />
<hr />
<div className="content">
<div className="row">
<div className="col-4">
<Card
image="https://dbkpop.com/wp-content/uploads/2020/06/weeekly_we_are_teaser_2_monday.jpg"
title="Monday"
description="Monday Weeekly Member"
/>
</div>
<div className="col-4">
<Card
image="https://dbkpop.com/wp-content/uploads/2020/06/weeekly_we_are_teaser_2_monday.jpg"
title="Monday"
description="Monday Weeekly Member"
/>
</div>
<div className="col-4">
<Card
image="https://dbkpop.com/wp-content/uploads/2020/06/weeekly_we_are_teaser_2_monday.jpg"
title="Monday"
description="Monday Weeekly Member"
/>
</div>
</div>
</div>
</div>
</div>
);
}
export default MainPage;
the problem is, how to pass State (movie) from Search Component to MainPage Component. so that I can render the data to MainPage Component
Data in React flows down, so you need to lift the state up.
Hence, the movie state should be in scope of MainPage:
function MainPage() {
const [ movie, setMovie ] = useState({})
// Set query results with setMovie
return <Search setMovie={setMovie} ... />
}
function Search(props) {
function searchHandler() {
axios.get(`${api.url}SearchMovie/${api.key}/${query}`)
.then(res => {
props.setMovie(res.data.results)
}).catch(err => {
console.log(err)
})
}
return (...);
}
export default Search;

onClick method called automatically react

I have a react component named <Filters /> which renders a component <PriceFilter />. The <PriceFilter /> component renders a <Filter /> component and some other JSX.
<Filters /> component:
const Filters = ({ price, setPrice }) => {
return (
<div>
<PriceFilter price={price} setPrice={setPrice} />
</div>
);
};
<PriceFilter /> component:
const PriceFilter = ({price, setPrice}) => {
const [show, setShow] = useState(false);
const toggleShow = () => setShow(!show);
return (
<div>
<Filter show={show} toggleShow={toggleShow}>
Price
</Filter>
{show && (
<FilterContainer>
<div>
<div onClick={() => setPrice('$')}>
<span>Inexpensive</span>
</div>
<div onClick={() => setPrice('$$')}>
<span>$$</span>
</div>
<div onClick={() => setPrice('$$$')}>
<span>$$$</span>
</div>
<div onClick={() => setPrice('$$$$')}>
<span>$$$$</span>
</div>
</div>
</FilterContainer>
)}
</div>
);
};
<Filter /> component:
const Filter = ({children, show, toggleShow}) => {
return (
<div>
<span>{children}</span>
{show ? <KeyboardArrowUpIcon onClick={toggleShow} /> : <KeyboardArrowDownIcon onClick={toggleShow} />}
</div>
);
};
Clicking on any of the options (Inexpensive/$$/$$$/$$$$) in the <PriceFilter /> component not only sets the state to the value passed ('$'/'$$'/'$$$'/'$$$$'), but it is also triggering the toggleShow function for some reason. Can someone please provide a solution to this so that the toggleShow function isn't called when clicking on the option.
The code works with minimal setup. See what is going wrong in higher level component Test where Filters is being rendered.
export const Test = () => {
const [price, setPrice] = useState('inexpensive')
return(
<Filters price={price} setPrice={setPrice} />
)
}
const Filters = ({ price, setPrice }) => {
return (
<div>
<PriceFilter price={price} setPrice={setPrice} />
</div>
);
};
const PriceFilter = ({price, setPrice}) => {
const [show, setShow] = useState(false);
const toggleShow = () => {
setShow(!show);
}
return (
<div>
<Filter show={show} toggleShow={toggleShow}>
{price}
</Filter>
{show && (
<div>
<div>
<div onClick={() => setPrice('$')}>
<span>Inexpensive</span>
</div>
<div onClick={() => setPrice('$$')}>
<span>$$</span>
</div>
<div onClick={() => setPrice('$$$')}>
<span>$$$</span>
</div>
<div onClick={() => setPrice('$$$$')}>
<span>$$$$</span>
</div>
</div>
</div>
)}
</div>
);
};
const Filter = ({children, show, toggleShow}) => {
return (
<div>
<span>{children}</span>
{show ? <div onClick={toggleShow}>up</div> : <div onClick={toggleShow}>down</div>}
</div>
);
};
It's working!! I didn't get why but there was a problem in the parent component. I had created a local component in the parent and rendered it there. I just extracted that local component in an independent component and it works now.

put elements into variable for naming (react)

I tried to put elements into CustomModal variable:
const CustomModal = (<div className="peoplelistpage-modal">
<div className="peoplelistpage-modal-content-empty" />
<div className="peoplelistpage-modal-content">
<CustomForm
krNameInput={this.state.krNameInput}
handleKrNameInput={this.handleKrNameInput}
enNameInput={this.state.enNameInput}
handleEnNameInput={this.handleEnNameInput}
positionInput={this.state.positionInput}
handlePositionInput={this.handlePositionInput}
departmentInput={this.state.departmentInput}
handleDepartmentInput={this.handleDepartmentInput}
doingInput={this.state.doingInput}
handleDoingInput={this.handleDoingInput}
btnValue="add"
onBtnClick={this.handlePersonAddBtn}
/>
</div>
<div className="peoplelistpage-modal-content-empty" />
</div>);
and used it in render() like this:
render() {
//...
{ CustomModal }
//...
but, got an error:
react-dom.development.js:57 Uncaught Invariant Violation: Objects are not valid as a React child (found: object with keys {CustomModal}). If you meant to render a collection of children, use an array instead.
Is there are any way to put elements into variable for naming?
Thanks.
----update-----
This is my full code of render :
render() {
const CustomModal = (<div className="peoplelistpage-modal">
<div className="peoplelistpage-modal-content-empty" />
<div className="peoplelistpage-modal-content">
<CustomForm
krNameInput={this.state.krNameInput}
handleKrNameInput={this.handleKrNameInput}
enNameInput={this.state.enNameInput}
handleEnNameInput={this.handleEnNameInput}
positionInput={this.state.positionInput}
handlePositionInput={this.handlePositionInput}
departmentInput={this.state.departmentInput}
handleDepartmentInput={this.handleDepartmentInput}
doingInput={this.state.doingInput}
handleDoingInput={this.handleDoingInput}
btnValue="add"
onBtnClick={this.handlePersonAddBtn}
/>
</div>
<div className="peoplelistpage-modal-content-empty" />
</div>);
const { people } = this.props.people.state;
return (
<React.Fragment>
{/* check login */}
{this.props.auth.state.isLoggedIn ? (
{ CustomModal }
) : (
<div />
)}
<div className="peoplelistpage-main">
<h1 className="peoplelistpage-title">people list</h1>
<div className="peoplelistpage-list-container">
{people.map((person, index) => (
<ul className="peoplelistpage-list-ul" key={index}>
<li className="peoplelistpage-list-li">
{`${index + 1}.`}{" "}
<Link to={`${this.props.location.pathname}/${person.id}`}>
{person.kr_name}
</Link>{" "}
<button onClick={this.handlePersonDeleteBtn(person.id)}>
<DeleteUserIcon />
</button>
</li>
</ul>
))}
</div>
<button onClick={this.openModal}>add</button>
</div>
</div>
) : (
<CustomNotPermittedForm />
)}
</React.Fragment>
);
}
Yes, you can use a div or a React.Fragment to group elements together. divs are added to the DOM while Fragments are not. But in order for this work with state, you will have to use a function, as a static variable does not get updated with state values.
const CustomModal = () => (
<React.Fragment> // <-- or div
<div className="peoplelistpage-modal">
<div className="peoplelistpage-modal-content-empty" />
<div className="peoplelistpage-modal-content">
<CustomForm
krNameInput={this.state.krNameInput}
handleKrNameInput={this.handleKrNameInput}
enNameInput={this.state.enNameInput}
handleEnNameInput={this.handleEnNameInput}
positionInput={this.state.positionInput}
handlePositionInput={this.handlePositionInput}
departmentInput={this.state.departmentInput}
handleDepartmentInput={this.handleDepartmentInput}
doingInput={this.state.doingInput}
handleDoingInput={this.handleDoingInput}
btnValue="add"
onBtnClick={this.handlePersonAddBtn}
/>
</div>
<div className="peoplelistpage-modal-content-empty" />
</div>
</React.Fragment>
);
Usage
render() {
// ...
<CustomModal />
// ...

Resources