Transfer Data from child component to parent component on React - javascript

I want to transfer a variable from search component as "child" to API component as "Parent". I did it with event for another part of codes but I don't know how did work exactly for a variable on my codes.
I want send "apiUrl" to parent. When user click on "current" get new location and generate new apiUrl, then send it to parent component for other stuff
Child:
import React from "react";
import "./Search.css";
const Search = function (props) {
const handleChange = (event) => {
props.onchange(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
props.onsubmit(event.target.value);
};
const navigation = (event) => {
event.preventDefault();
navigator.geolocation.getCurrentPosition(showPosition);
};
const showPosition = (position) => {
let latitude = position.coords.latitude;
let longitude = position.coords.longitude;
let latandlon = `lat=${latitude}&lon=${longitude}`;
let apiKey = "23422500afd990f6bd64b60f46cf509a";
let unit = "metric";
let apiUrl = `https://api.openweathermap.org/data/2.5/weather?${latandlon}&appid=${apiKey}&units=${unit}
`;
};
return (
<form className="form" onSubmit={handleSubmit}>
<div className="input-group">
<input
type="search"
className="form-control me-1"
placeholder="Enter City Name"
aria-label="City Name"
aria-describedby="basic-addon2"
onChange={handleChange}
/>
<div className="input-group-append">
<button className="btn btn-outline-secondary me-1" type="submit">
Search
</button>
<button
className="btn btn-outline-secondary me-1"
type="button"
onClick={navigation}
>
Current
</button>
</div>
</div>
</form>
);
};
export default Search;
Parent:
import React, { useState, useEffect } from "react";
import axios from "axios";
import Search from "./Search";
import ShowCurrentLocation from "./ShowCurrentLocation";
import HumidityAndWind from "./HumidityAndWind";
import CurrentStatus from "./CurrentStatus";
import ShowCurrentDay from "./ShowCurrentDay";
import CurrentDegree from "./CurrentDegree";
const Api = function (props) {
let [searchcity, setSearchcity] = useState("Tehran");
const [value, setValue] = useState("");
const [loader, setLoader] = useState(false);
const [weatherdata, setWeatherdata] = useState("");
const onchange = (data) => {
setValue(data);
};
const onsubmit = () => {
setSearchcity(value);
searchcity = value;
callApi();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(callApi, []);
function callApi() {
const apiKey = "23422500afd990f6bd64b60f46cf509a";
let units = "metric";
let apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${searchcity}&appid=${apiKey}&units=${units}`;
return axios.get(apiUrl).then(getWeatherData);
}
function getWeatherData(response) {
setWeatherdata({
temprature: Math.round(response.data.main.temp),
humidity: response.data.main.humidity,
wind: response.data.wind.speed,
description: response.data.weather[0].description,
city: response.data.name,
country: response.data.sys.country,
});
setLoader(true);
}
if (loader) {
return (
<div>
<div className="row">
<div className="col-md-9">
<Search
data1={searchcity}
onsubmit={(event) => {
onsubmit(event);
}}
data2={value}
onchange={(event) => {
onchange(event);
}}
/>
</div>
<div className="col-md-3 my-auto text-center">
<ShowCurrentLocation
data1={weatherdata.city}
data2={weatherdata.country}
/>
</div>
</div>
<div className="row my-auto">
<div className="col-md-7 my-auto">
<div className="row ">
<div className="col-6 my-auto text-start">
<div>
<HumidityAndWind
data1={weatherdata.humidity}
data2={weatherdata.wind}
/>
</div>
</div>
<div className="col-6 my-auto text-center">
<div>
<ShowCurrentDay />
</div>
</div>
</div>
</div>
<div className="col-md-5 my-auto">
<div className="row">
<div className="col-6 my-auto text-center">
<div>
<CurrentStatus data={weatherdata.description} />
</div>
</div>
<div className="col-6 my-auto text-center">
<CurrentDegree data={weatherdata.temprature} />
</div>
</div>
</div>
</div>
</div>
);
} else {
return "Loader";
}
};
export default Api;

You can't pass data or variable from your children to parent. But you can create some function from parent and pass it into a child that you want and the function is receive parameter for passing your data from children to parent. You can use useState to if you want, and pass that into your children component.
Example using useState:
ParentComponent
import { useState } from "react";
import ChildrenComponent from "./ChildrenComponent";
const ParentComponent = () => {
const [dataFromChild, setDataFromChild] = useState("");
console.log(dataFromChild);
return (
<div>
<ChildrenComponent setDataFromChild={setDataFromChild} />
</div>
);
};
export default ParentComponent;
ChildrenComponent
import { useState } from "react";
const ChildrenComponent = ({ setDataFromChild }) => {
const [data, setData] = useState("");
const handleChange = (e) => {
setData(e.target.value);
};
setDataFromChild(data);
return (
<div>
<label htmlFor="data"></label>
<input type="text" id="data" name="data" onChange={handleChange} />
<span>Its Data: </span>
<span>{data}</span>
</div>
);
};
export default ChildrenComponent;
so in above example we can access data on children component on parent component through function setDataFromChild using useState. Whenever the data onchildren change, the parent dataFromParent should be change to.

Related

How to pass a value from child to parent component in reactjs

I had a child component UploadImage.js and parent component Parent.js. I am uploading an image and want to pass the value of file name to the Parent.js component. How can I do so?
UploadImage.js
import React, { useEffect, useState } from 'react';
import { useDropzone } from 'react-dropzone';
.
.
.
const UploadImage = () => {
const [files, setFiles] = useState([]);
const { getRootProps, getInputProps } = useDropzone({
accept: {
'image/*': []
},
onDrop: acceptedFiles => {
setFiles(acceptedFiles.map(file => Object.assign(file, {
preview: URL.createObjectURL(file)
})));
}
});
//preview component
const thumbs = files.map(file => (
<div style={thumb} className="d-flex flex-row mt-1 col-12 mx-auto" key={file.name}>
<div style={thumbInner}>
<img
src={file.preview}
style={img}
// Revoke data uri after image is loaded
onLoad={() => { URL.revokeObjectURL(file.preview) }}
/>
</div>
</div>
)
);
//wanted to pass file[0].name to Parent Component
console.log(files.length > 0 ? files[0].name : "")
useEffect(() => {
// Make sure to revoke the data uris to avoid memory leaks, will run on unmount
return () => files.forEach(file => URL.revokeObjectURL(file.preview));
}, []);
return (
<section className="container">
<div {...getRootProps({ className: 'dropzone mx-3 text-center mt-4 mb-2 p-3 bg-light border border-primary border-1 rounded-4 ' })}>
<input {...getInputProps()} />
<p className='fw-bold text-primary'>Drag 'n' drop some files here, or click to select files</p>
</div>
<aside style={thumbsContainer} className="d-flex flex-row">
{thumbs}
</aside>
</section>
);
}
export default UploadImage;
And my Parent component is like this
import React, { useState} from "react";
import UploadImage from "../components/uploadImage";
const Parent = () => {
const [uploadFileName, setUploadFileName] = useState("");
return (
<div className="mx-3 mt-4 mb-2">
<UploadImage />
<h3 className="m-3">{uploadFileName} </h3>
</div>
);
};
export default UploadButton;
How can I display the file name from UploadImage.js to Parent.js in the uploadFileName state ???
you create a function in your parent element like:
const NameSetter = imageName => {
setUploadFileName(imageName);
}
and then send the NameSetter as a prop to your child element like:
<UploadImage nameHandler={NameSetter} />
and then in your child element you call the nameHandler prop like:
(call this when you get the name, for ex: on the callback of your backend )
props.nameHandler('name of your image');
you can use call back props to update the children to parent.
import React, { useState} from "react";
import UploadImage from "../components/uploadImage";
const Parent = () => {
const [uploadFileName, setUploadFileName] = useState("");
return (
<div className="mx-3 mt-4 mb-2">
<UploadImage setUploadFileName={setUploadFileName}/>
<h3 className="m-3">{uploadFileName} </h3>
</div>
);
};
export default UploadButton;
Then you can set whereever you want to call in child it will update in parent component. You can check through by adding consoling on the parent component.
Hey MagnusEffect you're almost correct, just make these changes-
In UploadImage.js-
const UploadImage = ({setUploadFileName}) => {
<input {...getInputProps()} onChange=
{(e)=>setUploadFileName(e.target.files[0].name)} />
}
While in Parent Component just pass setvalues-
const Parent = () => {
const [uploadFileName, setUploadFileName] = useState("");
return (
<div className="mx-3 mt-4 mb-2">
<UploadImage setUploadFileName={setUploadFileName} />
<h3 className="m-3">{uploadFileName} </h3>
</div>
);
}
Hope this code will help to solve your query if you still facing issue, just lemme know i will help you more. Thanks
You should move const [files, setFiles] = useState([]); to Parents.js and then pass them by Props for UploadImage.js.
// UploadImage Component
const UploadImage = (props) => {
const {files, onUpdateFiles} = props;
const { getRootProps, getInputProps } = useDropzone({
accept: {
'image/*': []
},
onDrop: acceptedFiles => {
onUpdateFiles(acceptedFiles.map(file => Object.assign(file, {
preview: URL.createObjectURL(file)
})));
}
});
...
}
// Parents component
const Parent = () => {
const [files, setFiles] = useState([]);
return (
<div className="mx-3 mt-4 mb-2">
<UploadImage files={files} onUpdateFiles={setFiles} />
{files.length > 0 && <h3 className="m-3">{files[0].name}</h3>}
</div>
);
};

How to show a div when a button is clicked

I just want to know, how to show a anything in a HTML body when the button is clicked. Is there anythin like echo in php
this is my appTodo.js code....
import React, { useState } from 'react'
export default function AddTodo() {
const [input, setInput] = useState("");
const onChange = (e) => {
setInput(e.target.value)
}
const handleOnClick = () => {
console.log(input)
setInput("")
}
return (
<div className='container my-3 col-6'>
<form>
<input className="form-control" onChange={onChange} type="text" placeholder="What to do?" value={input} />
<button id='addbtn' onClick={handleOnClick} type="button" className="btn btn-dark my-3">Add</button>
</form>
</div>
)
}
Just you need to create one variable to keep the state of the visibility of the div.
import React, { useState } from 'react'
export default function AddTodo() {
const [input, setInput] = useState("");
const [divVisibility, setDivVisibility] = useState(false);
const onChange = (e) => {
setInput(e.target.value)
}
const handleOnClick = () => {
setInput("")
setDivVisibility(true)
}
return (
<div className='container my-3 col-6'>
<form>
<input className="form-control" onChange={onChange} type="text" placeholder="What to do?" value={input} />
<button id='addbtn' onClick={handleOnClick} type="button" className="btn btn-dark my-3">Add</button>
</form>
{divVisibility &&
<div>
Your content
</div>
}
</div>
)
}
This code makes a state that can be changes between true and false by clicking the button. When false "componenet" = null, and when true "component" = (your component).
const [visible, setVisible] = useState(false);
function makeVisible() {
if(visible === false){
setVisible(true)
} else setVisible(false);
}
const component = visible == true ? <h1>SHOWN</h1> : null;
const buttonString = visible == true? "UnShow" : "Show"
return (
<div className="App">
<h1>Hello World!</h1>
{component}
<button onClick={makeVisible} >{buttonString}</button>
</div>
);
}

How to modify react button "More"?

I have the following React component:
import React from "react";
import { useState, useEffect } from "react";
import { TailSpin } from "react-loader-spinner";
function Pokemon({ name, url }) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then((r) => r.json())
.then(setData);
}, [url]);
const onClickButtonChange = () => {
let cardMore = document.querySelector(".card_more");
let cardMain = document.querySelector(".card_main");
cardMore.style.display = "block";
cardMain.style.display = "none";
};
return (
<div>
{data ? (
<div>
<div className="card card_main">
<div className="animate__animated animate__bounceInUp">
<div className="card-image">
<img src={data.sprites.front_default} alt="pokemon_img" />
<span className="card-title">{name}</span>
<button onClick={onClickButtonChange}>More</button>
</div>
<div className="card-content">
{data.abilities.map((n, index) => (
<p key={index}>{n.ability.name}</p>
))}
</div>
</div>
</div>
<div className="card card_more">
<p>{data.height}</p>
<p>{data.weight}</p>
</div>
</div>
) : (
<div>
<TailSpin type="Puff" color="purple" height={100} width={100} />
</div>
)}
</div>
);
}
export { Pokemon };
My implementation of the More button needs to display additional features (the card_more block). Right now this function only works on the very first element. I understand that in React this can most likely be done more correctly, but I don’t know how, so I use CSS styles.
P.S Edited:
I tried to use React features, maybe someone can tell me or does it make sense?
import React from "react";
import { useState, useEffect } from "react";
import { TailSpin } from "react-loader-spinner";
function Pokemon({ name, url }) {
const [data, setData] = useState(null);
const [show, setShow] = useState(false);
useEffect(() => {
fetch(url)
.then((r) => r.json())
.then(setData);
}, [url]);
const handleMore = async () => {
if (show === true) {
setShow(false);
} else if (show === false || !data) {
const r = await fetch(url);
const newData = await r.json();
setData(newData);
setShow(true);
}
};
return (
<div>
{data && show ? (
<div>
<div className="card card_main">
<div className="animate__animated animate__bounceInUp">
<div className="card-image">
<img src={data.sprites.front_default} alt="pokemon_img" />
<span className="card-title">{name}</span>
</div>
<div className="card-content">
{data.abilities.map((n, index) => (
<p key={index}>{n.ability.name}</p>
))}
</div>
</div>
<button onClick={handleMore}>More</button>
</div>
<div className="card card_more">
<p>{data.height}</p>
<p>{data.weight}</p>
</div>
</div>
) : (
<div>
<TailSpin type="Puff" color="purple" height={100} width={100} />
</div>
)}
</div>
);
}
export { Pokemon };
Youre right, this isn't the way you should do it in React. But your problem in your onClickButtonChange-Function is that youre only getting one element with document.querySelector(".card_more") and everytime you call it you get the same element back (No matter on which card you call it)
What you need to do is: Identify the single component elements. Thats most likely solved by passing a id/key value down via props and then putting this id on a parent-element (e.g. div.card) and you give it an id:
<div className="card card_main" id={props.keyvalue}>
....
</div>
And then in your onClickButtonChange-Function you call:
let cardMore = document.querySelector(`#${props.keyvalue} .card_more`);
...
This should give you the right element.

How can I pass a variable between two components in React?

I have a form component in React that I use to send data to a pg database.
This is my form script :
import bodyParser from 'body-parser';
import React, { Fragment, useState } from 'react';
import RatingStar from '../components/rating'
const InputData = () => {
const [name, setName] = useState('')
const [rating, setRating] = useState('')
const onSubmitForm = async(e) => {
e.preventDefault();
try {
const payload = {
name,
rating
}
const response = await fetch("path", {
method:"POST",
headers:{"Content-Type":"application/json"},
body:JSON.stringify(payload)
});
window.location = "/";
} catch (error) {
console.log(error.message);
}
}
return(
<Fragment>
<div className="container">
<h1 className="text-center mt-5">RATE</h1>
<form className="mt-5" onSubmit={onSubmitForm}>
<div className="form-group">
<input
placeholder="Name"
type='text'
className='form-control'
value={name}
onChange={e => setName(e.target.value)}
/>
</div>
<div className="form-group">
<div>
<RatingStar
value={}
/>
</div>
</div>
<div className="d-flex justify-content-center">
<button type="submit" className="d-flex btn btn-primary">Submit</button>
</div>
</form>
</div>
</Fragment>
);
}
export default InputData;
And this is my rating component :
import React, { useState } from 'react';
import { render } from 'react-dom';
import ReactStars from 'react-rating-stars-component'
import './style.css'
export default function RatingStar() {
const [rating, setRating] = useState("")
const secondExample = {
size: 50,
count: 5,
color: "black",
activeColor: "yellow",
value: 0,
a11y: true,
isHalf: true,
emptyIcon: <i className="far fa-star" />,
halfIcon: <i className="fa fa-star-half-alt" />,
filledIcon: <i className="fa fa-star" />,
onChange: (newValue) => {
console.log(`Example 2: new value is ${newValue}`);
setRating(newValue) // my try
}
};
return (
<div className="starComponent">
<ReactStars {...secondExample}
/>
</div>
);
}
So I was wondering how I could use newValue in the form component.
For now I tried using useState in the rating component but I can't access it from the form component to use it in my paylod.
Instead of keeping same state (i.e rating value) in two components, keep it in form component and pass it as prop to the Rating component.
Rating component will notify the parent(Form) component whenever the value gets changed by calling a function. This is called Lifting state up.
Here is the code for Rating component which gets rating and onRatingChange props from the form component. onRatingChange will be called with newValue from inside onChange function.
export default function RatingStar({ rating, onRatingChange }) {
const secondExample = {
size: 50,
count: 5,
color: "black",
activeColor: "yellow",
value: rating, // pass rating value here
a11y: true,
isHalf: true,
emptyIcon: <i className="far fa-star" />,
halfIcon: <i className="fa fa-star-half-alt" />,
filledIcon: <i className="fa fa-star" />,
onChange: (newValue) => {
console.log(`Example 2: new value is ${newValue}`);
// call onRatingChange function with new rating value
onRatingChange(newValue);
}
};
return (
<div className="starComponent">
<ReactStars {...secondExample} />
</div>
);
}
This is the code for Form component.
const InputData = () => {
const [name, setName] = useState('')
const [rating, setRating] = useState(0)
const onSubmitForm = async(e) => {
e.preventDefault();
try {
const payload = {
name,
rating
}
const response = await fetch("path", {
method:"POST",
headers:{"Content-Type":"application/json"},
body:JSON.stringify(payload)
});
window.location = "/";
} catch (error) {
console.log(error.message);
}
}
return(
<Fragment>
<div className="container">
<h1 className="text-center mt-5">RATE</h1>
<form className="mt-5" onSubmit={onSubmitForm}>
<div className="form-group">
<input
placeholder="Name"
type='text'
className='form-control'
value={name}
onChange={e => setName(e.target.value)}
/>
</div>
<div className="form-group">
<div>
<RatingStar
rating={rating}
onRatingChange={(newRating)=>{
// update rating value here when you get a new value
setRating(newRating);
}}
/>
</div>
</div>
<div className="d-flex justify-content-center">
<button type="submit" className="d-flex btn btn-primary">Submit</button>
</div>
</form>
</div>
</Fragment>
);
}
export default InputData;
You need to keep the state in InputData and pass it into RatingStar with the change handler.
const InputData = () => {
const [rating, setRating] = useState(0);
const handleRatingChange = (newRating) => {
console.log(`setting rating to ${newRating}`);
setRating(newRating);
}
return (
<RatingStar value={rating} onChange={handleRatingChange} />
);
};
Then RatingStar just uses the values from its parent.
const RatingStar = ({ value, onChange }) => {
const otherProps = {};
return (
<ReactStars {...otherProps} value={value} onChange={onChange} />
);
};
Here, RatingStar is a controlled component

getting empty item and same item when fetching data react hook

I ma creating app to get pokemons, first I save them in a list and after that show them, but I am getting empty card and after that the same pokemn is showing even if I am searching for another one.
Also I am getting distortion view when I use my component
import { useState, useEffect } from "react";
import "./App.css";
import { v4 } from "uuid";
import Button from "./Components/Button";
import Input from "./Components/Input";
import Label from "./Components/Label";
import Card from "./Components/Card";
import axios from "axios";
function App() {
// const [textFromInput, setTextFromInput] = useState("");
const [name, setName] = useState("");
const [nameFromButtonClick, setNameFromButtonClick] = useState("");
const [pokemon, setPokemon] = useState({
name: "",
picture: "",
id: 0,
type1: "",
type2: "",
});
const [list, setList] = useState([]);
const handleChange = (event) => setName(event.target.value);
const handleClick = () => {
setNameFromButtonClick(name);
setList([...list, pokemon]);
};
// const handleClick = () => setList([...list, pokemon]);
useEffect(() => {
axios
.get(`https://pokeapi.co/api/v2/pokemon/${name}/`)
.then((res) => {
console.log(res);
// setPokemon(res.data);
console.log("res.data=>", res.data);
setPokemon({
name: res.data.name,
picture: res.data.sprites.front_default,
id: res.data.id,
type1: res.data.types[0].type.name,
type2: res.data.types[1].type.name,
});
})
.catch((err) => {
console.log(err);
});
}, [nameFromButtonClick]);
return (
<div className="App">
<div>
<h1>Pokémon Effect</h1>
</div>
<div className="centered">
<div className="container">
{list.map((entry) => (
<Card key ={v4()}
name={entry.name}
picture={entry.picture}
id={entry.id}
type1={entry.type1}
type1={entry.type2}
/>
))}
</div>
<div className="dashboard">
<Input className="input" value={name} onChange={handleChange} />
<Button
className="getPokemon"
text="GetPokemon"
onClick={handleClick}
/>
<Label text={name} />
</div>
</div>
</div>
);
}
export default App;
this is my component Card, I don't know how to make it look like when I ma writing directly in app.js
export default function Card(props){
const{name, picture,id,type1,type2}=props
return(
<div className="card">
<div><img src={picture} alt={name} /></div>
<p>n:{id}</p>
<div> name={name}</div>
<div className="pokeType">
<div className="classType">type={type1}</div>
<div className="classType">type={type2}</div>
</div>
</div>
)
}

Categories

Resources