I'm currently building a react app which has a component slider and I need to pass the data back to the parent, the only fact is that the child is a little bit complex hook and I've been unable to find something similar that can help me to implement on my project, this is what I have:
The child
function valuetext(value) {
return `${value}`;
}
export default function RangeSlider() {
const classes = useStyles();
const [value, setValue] = React.useState([0, 100000]);
const handleChange = (event, newValue) => {
var val = setValue(newValue);
//I guess here is when I'm suposed to send the info to the parent
};
return (
<div className={classes.root}>
<Typography id="range-slider" gutterBottom>
Kilometers
</Typography>
<Slider
value={value}
max={500000}
min={0}
step={1000}
onChange={handleChange}
valueLabelDisplay="auto"
aria-labelledby="range-slider"
getAriaValueText={valuetext}
/>
<div id="seats-labes">
<span>0km</span>
<span>50.0000km</span>
</div>
</div>
);
}
The parent:
function WebFilter(props) {
return (
<div className="filter-web-section">
<Accordion className="filter-accordion">
<Card className="card-section">
<Card.Body>
<RangeSlider/>
</Card.Body>
</Card>
</Accordion>
</div>
)
}
export default WebFilter;
The grandfather:
class ResultModel extends Component {
render() {
return (
<div>
<h1>Texto de prueba + boton</h1> <button>+</button>
<div className="SiteHeader">
<Header/>
</div>
<div className="cars-result-content">
<div className="cars-result-content__filters">
<WebFilter
/>
</div>
<div className="car-result-content-list">
<div className="car-result-list__counter-cars">
<p>400 vehicles</p>
</div>
<div className="car-result-content-list__statBar">
<StatBar/>
</div>
<div className="cars-result-page-list__ListCars">
<ResultsView/>
</div>
</div>
</div>
</div>
)
}
}
I've been reading about declaring the hook constants at the very first component (grandfather) but I haven't been able to find a way to pass the data through the father. Thanks in advance for any hint or help.
The question is a bit short on specifics, but from what I can gather, you just need to pass down a function from component 1 through component 2 to component 3.
It's pretty straightforward actually.
In your grandpa component, create a function you want to pass:
class ResultModel extends Component {
const func1 = (data) => {console.log(data)}
render() {
...
Pass it down to father:
...
<WebFilter func1={func1} />
...
In the father component, get func1 and pass it down to child:
function WebFilter(props) {
const {func1} = props;
return (
<div className="filter-web-section">
<Accordion className="filter-accordion">
<Card className="card-section">
<Card.Body>
<RangeSlider func1={func1} />
</Card.Body>
</Card>
</Accordion>
</div>
)
}
Then in child call it like so:
export default function RangeSlider({func1}) {
const classes = useStyles();
const [value, setValue] = React.useState([0, 100000]);
const handleChange = (event, newValue) => {
var val = setValue(newValue);
//I guess here is when I'm suposed to send the info to the parent
func1("your data")
};
...
...
If you want to learn something read about react concept called lifting the state up.
Read about lifting state up in react documentation
Or just google it read one or two articles if still don't get it then post a comment I'll write full code.
Related
I'm following this tutorial on YouTube https://youtu.be/b9eMGE7QtTk
The full code can be found here: https://gist.github.com/adrianhajdin/997a8cdf94234e889fa47be89a4759f1
The tutorial was great, but it didn't split all the functionalities into components which is React used for (or I'm so lead to believe).
So we have the App.js
import React, { useState, useEffect } from "react";
import MovieCard from "./MovieCard";
import SearchIcon from "./search.svg";
import "./App.css";
const API_URL = "http://www.omdbapi.com?apikey=b6003d8a";
const App = () => {
const [searchTerm, setSearchTerm] = useState("");
const [movies, setMovies] = useState([]);
useEffect(() => {
searchMovies("Batman");
}, []);
const searchMovies = async (title) => {
const response = await fetch(`${API_URL}&s=${title}`);
const data = await response.json();
setMovies(data.Search);
};
return (
<div className="app">
<h1>MovieLand</h1>
<div className="search">
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search for movies"
/>
<img
src={SearchIcon}
alt="search"
onClick={() => searchMovies(searchTerm)}
/>
</div>
{movies?.length > 0 ? (
<div className="container">
{movies.map((movie) => (
<MovieCard movie={movie} />
))}
</div>
) : (
<div className="empty">
<h2>No movies found</h2>
</div>
)}
</div>
);
};
export default App;
MovieCards.jsx is as follows:
import React from 'react';
const MovieCard = ({ movie: { imdbID, Year, Poster, Title, Type } }) => {
return (
<div className="movie" key={imdbID}>
<div>
<p>{Year}</p>
</div>
<div>
<img src={Poster !== "N/A" ? Poster : "https://via.placeholder.com/400"} alt={Title} />
</div>
<div>
<span>{Type}</span>
<h3>{Title}</h3>
</div>
</div>
);
}
export default MovieCard;
The app works, but I want to move className="search" to be its own component like Search /.
The code I end up having in App.js is
//at the top of App.jx
import Search from "./Search"
// in const App
<Search prop={searchMovies}/>
And in the new Seach / component
import { useState } from "react";
import SearchIcon from './search.svg';
const Search = ( prop ) => {
const [searchTerm, setSearchTerm] = useState("");
return (
<div className="search">
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search"
/>
<img
src={SearchIcon}
alt="search"
onClick={() => prop(searchTerm)}
//props used to be searchMovies
/>
</div>
)
}
export default Search;
When typing something in the search field on the app and clicking on the search icon I get the following error:
prop is not a function
If my research has been correct, I need to use a constructor and super()
But it seems like the constructor needs to be called in a class Search instead of const Search as it breaks the code. Is that the case or is there a way to use the constructor in a function component, or is there something else completely that I should do?
Also, if there is a great tutorial you could recommend for super() I'd be really grateful.
Other thing that I want to do is to make a Results component or call it whatever that would have the {movies?.length > 0 ? ( part of the code, but I feel like that will be a different headache.
Basically what I want is to have:
const App = () => {
return (
<div className="app">
<h1>Movie Site</h1>
<Search />
<Results />
</div>
);
};
Or as shown in the picture
Hope all this makes sense. Also, I want to preface that I do not expect anyone to write the code for me, but if it helps me understand this it's appreciated. YT tutorials are appreciated as well.
Okay, after a push in the right direction from jonrsharpe and renaming the props into random things I figured it out.
As jonrsharpe said, my function is prop.prop, so if I wanted to call searchTerm in
onClick={() => prop(searchTerm)}
it should be
onClick={() => prop.prop(searchTerm)}
Now, that works, but looks silly. So renaming the first "prop" in prop.prop and the prop in const Search to searchOnClick leaves searchOnClick.prop(searchTerm) which still works. Great.
Then in App.js renaming prop in Search prop={searchMovies} to searchOnClick={searchMovies} needs to be followed by renaming searchOnClick.prop in Search.jsx to searchOnClick.searchOnClick.
Lastly, we want to destructure the props as jonrsharpe said.
const Search = ( searchOnClick ) => {
would become
const Search = ( {searchOnClick} ) => {
That allows us to remake searchOnClick.searchOnClick(searchTerm) to searchOnClick(searchTerm) only.
The whole point is that the prop calls the whole componentName variable=value but it doesn't take the value of the variable automatically so it needs to be called like prop.variable until destructured where it can be called as variable only.
Now that I figured this out it feels silly spending two days on this. Thanks to jonrsharpe again, and hope this helps to someone else in the future.
I'm new to React. I'm trying to add additional functionality of deleting the record from the list by setting the value.
here is my App.js
import React, { useState } from "react";
import data from "./data";
import List from "./List";
function App() {
const [movies, setMovie] = useState(data);
return (
<main>
<section className='container'>
<h3>{movies.length} Movies to Watch</h3>
<List movies={movies} setMovie />
<button onClick={() => setMovie([])}>clear all</button>
</section>
</main>
);
}
export default App;
In List.js, Im trying to delete the record when clicking on Watched button. Can I call setMovie inside the List component? is it a correct way?
List.js
import React from "react";
const List = ({ movies }, setMovie) => {
return (
<>
{movies.map((movie) => {
const { id, name, year, image } = movie;
return (
<article key={id} className='person'>
<img src={image} alt={name} />
<div>
<h4>{name}</h4>
<button
className='btn'
onClick={(id) =>
setMovie(movies.filter((movie) => movie.id !== id))
}
>
watched
</button>
<p>{year}</p>
</div>
</article>
);
})}
</>
);
};
export default List;
You have two mistakes in your code. First:
<List movies={movies} setMovie />
This shorthand assigns a value of true to setMovie. To assign the setMovie function to it, you must instead do:
<List movies={movies} setMovie={setMovie} />
And secondly this:
const List = ({ movies }, setMovie) => {
Should be this:
const List = ({ movies, setMovie }) => {
try:
<List movies={movies} setMovie={setMovie} />
this way the funcition will appear in the List component as a prop.
The way you were doing, it will just appear as true
I am new to learning react and am stuck with this doubt. I have a simple button and on click of that button I want to add some text (or any other html) element. The console log statement is getting executed but the div tag is not getting rednered. This is my following code.
function App() {
const executeMe = () => {
console.log("executed")
return(
<div> Clicked here</div>
)
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
</div>
</LocalizationProvider>
);
}
export default App;
I know that I am missing out something which may be very simple. Please help me fix this. Thanks
Your looking at React wrongly, it doesn't work this way. You can do this instead.
import { useState } from "react";
function App() {
const [clicked, setClicked] = useState(false);
const [lines, setLines] = useState([]);
const executeMe = () => setClicked(!clicked);
const onAddLine= () => setLines(lines.concat("New line (Could be unique)"));
return (
<div className="App">
Hello world
{/* METHOD A */}
{!clicked && <button onClick={executeMe }>Click me</button>}
{clicked && <div>Clicked here</div>}
<br />
{/* METHOD B */}
<button onClick={executeMe}>{clicked ? "Clicked here" : "Click me"}</button>
<br />
{/* ADDITIONAL FUN STUFF WITH SEPERATE BUTTON */}
<button onClick={onAddLine}>Add new line</button>
<br />
{lines.map((line, x) => {
return(
<div key = {x}>{x+1} : {line}</div>
);
})}
</div>
);
};
export default App;
You can render that div by using state instead and reset it on the next click.
function App() {
const [showDiv, setShowDiv] = useState(false);
const executeMe = () => {
console.log("executed");
setShowDiv(!showDiv);
};
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{showDiv && <div> Clicked here</div>} {/* render div once showDiv state is true */}
</div>
</LocalizationProvider>
);
}
export default App;
You should add a state value to check when the button has been pressed.
Here is more information about how to use useState hook.
function App() {
const [isButtonPressed, setIsButtonPressed] = useState(false);
const executeMe = () => {
console.log("executed");
setIsButtonPressed(true);
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{isButtonPressed && <div>Clicked here</div>}
</div>
</LocalizationProvider>
);
}
export default App;
There are many ways to achieve it.
First React is just JavaScript, most JS code will work within the component.
But some dev might find it not so React which is weird for me :)
So here are the two examples that you might try:
function App() {
const [list, setList] = React.useState([])
const handleAddLine = () => {
const lists = document.getElementById('lists')
const li = document.createElement('li')
li.textContent = 'hey'
lists.append(li)
}
const handleAddLineReactish = () => {
setList(prevList => {
return prevList.concat(<li>hey</li>)
})
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={handleAddLine}>Add</button>
<ul id='lists'></ul>
<button onClick={handleAddLineReactish}>Add Reactish</button>
<ul>
{list.length > 0 && list.map((l, i) => {
return (
<li key={i}>{l}</li>
)
})}
</ul>
</div>
);
}
sandbox URL: https://codesandbox.io/s/funny-sun-7f4epn?file=/src/App.js
For something like this we use a react hook called "useState".
In "useState" we store a something and on the basis of that we do stuff like to show, hide and more.
See the image
you can write that html code in another component and import it into the current file you can make useState to check the value is 'visible' with type 'true/false' to check the state when the button is click.
code example
import React, { useState } from "react";
function App() {
const [showText, setShowText] = useState(false);
const executeMe = () => {
console.log("executed")
setShowText(true);
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{showText ? <Text /> : null}
</div>
</LocalizationProvider>
);
}
const Text = () => <div>You clicked the button!</div>;
export default App;
Hello,
I am coming to you today for the first time because I have not found a solution to my problem.
I have been using react for a few weeks, Don't be too cruel about the quality of my code 😁.
Problem :
I am looking to access the state of a parent from their children.So I want to be able to access the setHeight function and the height variable for example from a child component.
Please note :
However, to keep some flexibility, I don't want to have any Components inside our.
I looked at redux to be able to do this, but the problem is that the data is global so creating multiple dropdowns would not be possible.
(Unless I didn't understand too much, redux is quite complex)
Diagram :
I have created a diagram to explain it a little better.,
I'd like the children of DropdownMenu to be able to access the state of the latter, Also, the different Dropdowns must have their own state independently.
So ideally I want to keep the same structure as find very flexible, and the possibility to create several dropdown.
Code :
I Share my four components :
export default function Navbar () {
return (
<nav className={styles.navbar}>
<ul className={styles.navbarNav}>
<NavItem icon={<NotificationsIcon />} />
<NavItem icon={<AccessTimeFilledIcon />} />
<NavItem icon={<FileOpenIcon />}>
<DropdownMenu>
<DropdownSubMenu menuName="Home">
<DropdownItem>My Profile</DropdownItem>
<DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="pages">Pages</DropdownItem>
<DropdownItem>IDK</DropdownItem>
<DropdownItem>Test</DropdownItem>
</DropdownSubMenu>
<DropdownSubMenu menuName="pages">
<DropdownItem>Pages</DropdownItem>
<DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="home">Home</DropdownItem>
</DropdownSubMenu>
</DropdownMenu>
<DropdownMenu>
<DropdownSubMenu menuName="config">
<DropdownItem>Foo</DropdownItem>
<DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="theme">Configuration</DropdownItem>
<DropdownItem>Bar</DropdownItem>
<DropdownItem>Baz</DropdownItem>
</DropdownSubMenu>
<DropdownSubMenu menuName="theme">
<DropdownItem>Hi StackOverflow</DropdownItem>
<DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="config">Theme</DropdownItem>
</DropdownSubMenu>
</DropdownMenu>
</NavItem>
</ul>
</nav>
);
};
type Props = {
children?: React.ReactNode | React.ReactNode[];
leftIcon?: React.ReactNode | JSX.Element | Array<React.ReactNode | JSX.Element>;
rightIcon?: React.ReactNode | JSX.Element | Array<React.ReactNode | JSX.Element>;
goToMenu?: string;
goBack?: boolean;
OnClick?: () => void;
};
export default function DropdownItem({ children, leftIcon, rightIcon, goToMenu, goBack, OnClick }: Props) {
const handleClick = OnClick === undefined ? () => { } : OnClick;
return (
<a className={styles.menuItem} onClick={() => {
goToMenu && setActiveMenu(goToMenu);
setDirection(goBack ? 'menu-right' : 'menu-left');
handleClick();
}}>
<span className={styles.iconButton}>{leftIcon}</span>
{children}
<span className={styles.iconRight}>{rightIcon}</span>
</a>
);
}
type Props = {
menuName: string;
children: React.ReactNode | React.ReactNode[];
}
enum Direction {
LEFT = 'menu-left',
RIGHT = 'menu-right'
}
export default function DropdownSubMenu (props: Props) {
const [direction, setDirection] = useState<Direction>(Direction.LEFT);
const calcHeight = (element: HTMLElement) => {
if (element) setMenuHeight(element.offsetHeight);
};
return (
<CSSTransition in={activeMenu === props.menuName} unmountOnExit timeout={500} classNames={direction} onEnter={calcHeight}>
<div className={styles.menu}>
{props.children}
</div>
</CSSTransition>
);
}
type Props = {
children: React.ReactNode | React.ReactNode[];
}
export default function DropdownMenu (props: Props) {
const [activeMenu, setActiveMenu] = useState<string>('home');
const [menuHeight, setMenuHeight] = useState<number | null>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const child = dropdownRef.current?.firstChild as HTMLElement;
const height = getHeight(child);
if (height)
setMenuHeight(height);
}, []);
return (
<div className={styles.dropdown} style={{ height: `calc(${menuHeight}px + 2rem)` }} ref={dropdownRef}>
{props.children}
</div>
);
}
Conclusion :
More concretely I don't know what to put instead :
In DropdownSubMenu to set the menu height (setMenuHeight), and gets the active menu (activeMenu).
In DropdownItem, set the active menu, (setActiveMenu) and set the direction of the CSS animation (setDirection).
Source :
My code is adapted from these sources, But I want to make this code more professional, flexible and polymorphic :
https://github.com/fireship-io/229-multi-level-dropdown
I've been tried :
I tried to look at Redux, but I understood that it was only state global.
So it doesn't allow to define a different context for each component.
I tried to look at React 18, without success.
I have searched the StackOverflow posts, I have searched the state retrieval from the parents.
The use of components inside a component solves in effect the problem but we lose all the flexibility.
There are multiple ways to access a parent state from its children.
Pass the state as props
The preferred way is to pass the state and/or the change function to the children.
Example :
const App = () => {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<div>
<button onClick={handleOpen}>Open modal</button>
<Modal onClose={handleClose} open={open} />
</div>
);
};
const Modal = ({ open, onClose }) => (
<div className={open ? "open" : "close"}>
<h1>Modal</h1>
<button onClick={onClose}>Close</button>
</div>
);
ReactDOM.render(<App />, document.querySelector("#app"));
Demo: https://jsfiddle.net/47s28ge5/1/
Use React Context
The first method becomes complicated when the children are deeply nested and you don't want to carry the state along the component tree.
You can then share a state across multiple children by using context.
const AppContext = React.createContext(undefined);
const App = () => {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<AppContext.Provider value={{ open, onClose: handleClose }}>
<div>
<button onClick={handleOpen}>Open modal</button>
<Modal />
</div>
</AppContext.Provider>
);
};
const Modal = () => {
const { open, onClose } = React.useContext(AppContext);
return (
<div className={open ? "open" : "close"}>
<h1>Modal</h1>
<button onClick={onClose}>Close</button>
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#app"));
Demo: https://jsfiddle.net/dho0tmc2/3/
Using a reducer
If your code gets even more complicated, you might consider using a store to share a global state across your components.
You can take a look at popular options such as:
Mobx: https://mobx.js.org/ (my personal favorite)
Redux: https://redux.js.org/
RxJS: https://rxjs.dev/
I can say welcome to react in this moment and i glad for you
OK, i could understand what is your problem.
but there isn't problem and this bug cause from your low experience.
As i understand you want to click on a dropdown and open it.
and here we have nested dropdown.
I think it's your answer:
You should declare a state on each dropdown and don't declare state in parent.
In my react program i have made a DrinkList.js in which i show the images of drink. but when i start make it clickable using Onclick and passing a function to display its name in console. error start flooding.
import React from "react";
// import index from "./src"
const DrinkList = (props) => {
const handleTextShow = (drinkDetail) => {
console.log(drinkDetail.strGlass);
};
return (
<>
{props.drinks.map((drink, index) => (
<div>
<img src={drink.strDrinkThumb} alt="drink" width="300" height="300">
{/* hello */}
onClick={() => handleTextShow(handleTextShow)}
</img>
{/* <h4> {drink.strInstructions} </h4> */}
{/* <Details /> */}
</div>
))}
</>
);
};
export default DrinkList;
say strGlass is detail strDrinkThumb is photo.
The problem is that , in handleTextShow function, instead of passing name of drink to want to console, you're passing handleTextShow function as argument, to solve it replace handleTextShow argument with drink name,
onClick={() => handleTextShow(drink.name)}