Grabbing the Value of a Dynamically Created Object - reactjs

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.

Related

Retrieve form input values using React Children as a wrapper

I need to retrieve the input form data using the wrapper save button (Togglable component) and not a submit button inside a form component.
I have this form.
const BasicDataForm = () => {
return (
<form>
<div>
<label htmlFor="nameInput">Your Name: </label>
<input placeholder="Write your name here" type="text" id="nameInput" />
</div>
<div>
<label htmlFor="ageInput">Your Age: </label>
<input placeholder="Write your age here" type="number" id="ageInput" />
</div>
</form>
);
};
And the wrapper
const Togglable = ({ title, children }) => {
const [visible, setVisible] = useState(false);
const hideWhenVisible = { display: visible ? 'none' : '' };
const showWhenVisible = { display: visible ? '' : 'none' };
return (
<header>
<h2>{title}</h2>
<div style={hideWhenVisible}>
<button onClick={() => setVisible(true)}>Show</button>
</div>
<div style={showWhenVisible}>
<button>Save</button> {/* This button needs to retrieve all input form data from children */}
<button onClick={() => setVisible(false)}>Close</button>
{children}
</div>
</header>
);
};
And the main component
const App = () => {
return (
<>
<h1>Form Wrapper</h1>
<Togglable title="Basic Data Form">
<BasicDataForm />
</Togglable>
</>
);
};
I don't know if it's necessary to add a useState inside the form component. I also tried adding a new prop in the Togglable component that bind the onClick event of the save button to the onSubmit event of the form, but not work because there is no submit button inside form component.
The solution was much easier than I thought. You only need to wrap the entire children inside a <form> label and render only the content of the inputs. For every children a button will be added and you will manage the form outside the wrapper.
const Togglable = ({ title, children, handleSubmit }) => {
const [visible, setVisible] = useState(false);
const hideWhenVisible = { display: visible ? 'none' : '' };
const showWhenVisible = { display: visible ? '' : 'none' };
return (
<header>
<h2>{title}</h2>
<div style={hideWhenVisible}>
<button onClick={() => setVisible(true)}>Show</button>
</div>
<div style={showWhenVisible}>
<button onClick={() => setVisible(false)}>Close</button>
<form onSubmit={handleSubmit}>
{children}
<button type="submit">Save</button>
</form>
</div>
</header>
);
};

Input field not cleared after using useState with onClick

I have a React app, where I'm using an input field as a searchbar, which upon typing, returns a list of products, and upon clicking any product takes you to that product page. I want to clear the typed text in the searchbar after the redirection to new page has happened but I haven't been able to achieve this yet.
I've tried many methods and went over similar posts, but I don't know what am I doing wrong as text is never cleared.
I'm using Material UI for rendering the list and have imported everything as needed.
Below is my code:
Navbar component (contains searchbar)
const Navbar = () => {
const [text, setText] = useState('');
const [liopen, setLiopen] = useState(true);
const getText = (text) => {
setText(text);
setLiopen(false);
};
const handleClick2 = (e) => {
setText('');
setLiopen(true)
};
return (
<header>
<nav>
<div className="middle">
<div className="nav_searchbar">
<span className="search_icon">
<SearchIcon id="search" />
</span>
<input
type="text"
onChange={(e) => getText(e.target.value)}
name=""
placeholder="Search for products, categories, ..."
id=""
/>
</div>
{text && (
<List className="extrasearch" hidden={liopen}>
{products
.filter((product) =>
product.title.toLowerCase().includes(text.toLowerCase())
)
.map((product) => (
<ListItem>
<NavLink
to={`/getproductsone/${product.id}`}
onClick={(e) => {handleClick2(e)}}
>
{product.title}
</NavLink>
</ListItem>
))}
</List>
)}
</nav>
</div>
</header>
);
};
export default Navbar;
You need to set the value of the input if you want it controlled by the component state.
<input value={text}
type="text"
onChange={(e) => getText(e.target.value)}
name=""
placeholder="Search for products, categories, ..."
id=""
/>

How to pass state properties from component to component

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

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;

Select multiple checkboxes without toggling

I am new to react. I am trying to create a component that hides a div when a checkbox is clicked. The problem I am having is, if I introduce more than one checkbox the divs visibility is toggled. Is there an alternative way to allow the selection of all checkboxes?
The functionality should be: click a checkbox or multiple > div remains hidden until all checkboxes are cleared or unchecked.
JSX:
import React, { useState } from 'react';
function reactComponent() {
const [isChecked, setIsChecked] = useState(false);
const toggle = () => setIsChecked(!isChecked);
return (
<div>
<div>
<input type="checkbox" id="option" name="option" onClick={toggle} />
<label for="scales">option</label>
</div>
<div>
<input type="checkbox" id="option" name="option" onClick={toggle} />
<label for="scales">option</label>
</div>
<div className={isChecked ? "hide" : "block "}>
<h3 className="red bold">Content</h3>
<p>lorem Ipsum</p>
</div>
</div>
)
}
export default reactComponent
To achieve what you've described you could use controlled inputs for checkboxes and have a separate piece of state for every checkbox. Here is Codesandbox demo of an example below (you can change some to every in shouldShow flag if you need to show the div if and only if all the checkboxes are checked).
function App() {
const [isChecked, setIsChecked] = useState({ option1: false, option2: false })
const toggle = ({ target: { name } }) =>
setIsChecked({ ...isChecked, [name]: !isChecked[name] })
// Are there any checked ones?
const shouldShow = Object.values(isChecked).some(val => val)
return (
<>
<div>
<input
type="checkbox"
id="option1"
name="option1"
checked={isChecked.option1}
value={isChecked.option1}
onClick={toggle}
/>
<label for="option1">option1</label>
</div>
<div>
<input
type="checkbox"
id="option2"
name="option2"
checked={isChecked.option2}
value={isChecked.option2}
onClick={toggle}
/>
<label for="option2">option2</label>
</div>
<div className={shouldShow ? "hide" : "block "}>
<h3 className="red bold">Content</h3>
<p>lorem Ipsum</p>
</div>
</>
)
}
If you render your checkboxes from an array, you can always check if the length of that array is the same as the length of the checked array kept in state.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const myOptions = ["option1", "option2", "option3", "option4"];
const [myChecked, setMyChecked] = useState([]);
const toggle = e => {
e.persist();
if (e.target.checked) {
setMyChecked(oldArray => [...oldArray, e.target.name]);
} else {
setMyChecked(oldArray => oldArray.filter(item => item !== e.target.name));
}
};
const showDiv = () => {
return myChecked.length === myOptions.length;
};
return (
<div>
{myOptions.map(option => (
<div>
<label>
<input type="checkbox" name={option} onChange={toggle} />
{option}
</label>
</div>
))}
<div className={showDiv() ? "block" : "hide "}>
<h3>Content</h3>
<p>lorem Ipsum</p>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Codesandbox

Resources