Hello I got stuck during creating app using hooks
I do not why but my Component does not download a state from my Context Component or maybe my initial state does not update correctly. Does somebody have any idea what's going on?
Context Component:
import React, { createContext, useState } from 'react';
export const WeatherDataContext = createContext();
const WeatherDataContextProvider = (props) => {
const [weather, setWeather] = useState(
{
city: null,
temp: null
}
)
const addWeather = (city, temp) => {
setWeather({
city,
temp
})
}
return (
<WeatherDataContext.Provider value={{weather, addWeather}}>
{props.children}
</WeatherDataContext.Provider>
)
}
export default WeatherDataContextProvider
Form - axios - Component:
import React, {useContext, useState} from 'react';
import { WeatherDataContext } from '../context/WeatherDataContext';
import axios from 'axios'
import {Link} from 'react-router-dom'
const WeatherForm = () => {
const {addWeather} = useContext(WeatherDataContext);
const [value, setValue] = useState('')
const handleChange = (e) => {
e.preventDefault();
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${value}&appid=${KEY}&units=metric`)
.then(res => {
addWeather(res.data.name, res.data.main.temp)
})
}
return (
<div class='weather-form'>
<form onSubmit={handleChange}>
<input placeholder='City' onChange={(e) => setValue(e.target.value)} value={value} required/>
<Link to='/weather'><button>Search</button></Link>
</form>
</div>
)
}
export default WeatherForm
And final component where I want to use my update state
import React, {useContext, useState} from 'react';
import { WeatherDataContext } from '../context/WeatherDataContext';
const WeatherFront = () => {
const {weather} = useContext(WeatherDataContext)
console.log(weather)
return (
<div class='weather-front'>
<h1>City: {weather.city}, Temperatura: {weather.temp}</h1>
</div>
)
}
export default WeatherFront
Your button is not submitting the form - it navigates away from the page instead.
So handleChange is not being called.
You can call it from buttons onClick instead of forms onSubmit. Be sure to omit e.preventDefault() then, so that parent Link can still navigate.
const WeatherForm = () => {
const { addWeather } = useContext(WeatherDataContext)
const [value, setValue] = useState('')
const handleChange = (e) => {
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${value}&appid=${KEY}&units=metric`)
.then(res => {
addWeather(res.data.name, res.data.main.temp)
})
}
return (
<div class="weather-form">
<form >
<input
placeholder="City"
onChange={(e) => setValue(e.target.value)}
value={value}
required
/>
<Link to="/weather">
<button onClick={handleChange}>Search</button>
</Link>
</form>
</div>
)
}
Be sure to wrap both pages inside the same context:
<WeatherDataContextProvider>
<Router>
<Switch>
<Route path="/weather">
<WeatherFront></WeatherFront>
</Route>
<Route path="/">
<WeatherForm></WeatherForm>
</Route>
</Switch>
</Router>
</WeatherDataContextProvider>
Related
I used navigate in react-router v6. But It doesn't navigate to /search. I got data from API and want to compare it with the value of the search bar. If they are equal, navigate to /search; if they're not, navigate to /searchNull .I checked in the console. They were equal. But It is always navigated to /searchNull path. Do you have any solution?
SearchInput.js
import "./SearchInput.css";
import Search from "../../../assets/Images/icon/search-normal.svg";
import { useNavigate } from "react-router-dom";
import { React , useRef , useState } from "react";
import { useAppContext } from "../../../Context/SearchContext/SearchContext";
const SearchInput =(props)=>{
const [inputChange , setInputChanage ] =useState("");
const inputRef = useRef(null);
const navigate = useNavigate();
const {setSearchValue , bookData } = useAppContext();
const inputChangeHandler = (event) => {
setInputChanage(event.target.value)
};
const handleKeyDown = (event) => {
if (event.key === "Enter"){
setSearchValue(inputRef.current.value);
bookData.map((item) => {
if( item.name.toString() === inputChange.toString()){
navigate("/search");
}
else{
navigate("/searchNull")
}
})
}
}
return(
<div className="search-input-container">
<input
className="search-input"
ref={inputRef}
onChange={inputChangeHandler}
value={inputChange} type="text"
onKeyDown={handleKeyDown} placeholder= {props.placeholder}
/>
<button className="search-icon" onClick={handleClick}>
<img src={Search} width="100%" height="100%" alt="search icon" />
</button>
</div>
)
export default SearchInput;
I called API in Context.js :
import React, { createContext, useContext, useEffect, useState } from "react";
import axios from "axios";
const CoinsContext = createContext({});
const SearchContext = ({ children }) => {
const [searchValue, setSearchValue] = useState(' ');
const [bookData , setBookData ] = useState([]);
useEffect(()=>{
axios
.post('/books/list/all').then((response)=>{
const bookApi = [];
response.data.data.map((item)=>{
bookApi.push({
id:item.id,
name:item.name,
})
})
setBookData(bookApi)
})
},[])
return (
<CoinsContext.Provider value={{ searchValue, setSearchValue ,bookData ,setBookData
}}>
{children}
</CoinsContext.Provider>
);
};
export const useAppContext = () => useContext(CoinsContext);
export default SearchContext;
I've been working on variations of this problem for a while now. Basically, I have a child component that can update existing data. It updates data with no problems and the parent re-renders accordingly. The child component doesn't re-render though. So, on advice given on this site, I've tried lifting the state. I'm passing down props down to the two child components I'm running. My problem is the "EditStudent" component. I can't seem to destructure/get the "setStudent" function that's being passed down from the parent component so I'm getting a "setStudent is not a function error" no matter how I try to call this function. Any advice is greatly appreciated as it's been driving me slowly insane on how to figure this out.
Here's the code I've been working with so far.
Parent component "StudentList"
import React, { useState } from "react";
import { useQuery } from "#apollo/client";
import { getStudents } from "../queries";
import StudentDetails from "./StudentDetails";
import DeleteStudent from "./DeleteStudent";
import EditStudent from "./EditStudent";
const StudentList = () => {
const [selectedStudent, setSelectedStudent] = useState("");
const { loading, error, data } = useQuery(getStudents);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error!</p>;
const handleClick = (student)=> {
//console.log(student)
setSelectedStudent(student);
};
let filteredStudents = [];
//console.log(data.students)
for(let i = 0; i < data.students.length; i++){
//console.log(data.students[i].class.name)
if(data.students[i].class.name === "1FE1"){
//console.log(data.students[i].name)
filteredStudents.push(data.students[i])
}
}
console.log(selectedStudent.id);
return (
<div>
<ul id="student-list">
{data.students.map((student) => (
<li key={student.id} onClick={(e) => handleClick(student)}>{student.name}</li>
))}
</ul>
{
selectedStudent ? <div>
<StudentDetails student={selectedStudent} setStudent={setSelectedStudent}/>
</div>
: <p>No Student Selected</p>
}
</div>
);
};
export default StudentList;
This is "StudentDetails" - a component receiving the "studentDetails" prop and also has two other components nested inside - "DeleteStudent" and "EditStudent"
import React from "react";
import { useEffect, useState } from "react";
import { getStudentQuery } from "../queries";
import { useQuery } from "#apollo/client";
import DeleteStudent from "./DeleteStudent"
import EditStudent from "./EditStudent";
const StudentDetails = ( selectedStudent )=> {
const {setStudent} = selectedStudent;
console.log(selectedStudent)
//const [astudent, setStudent] = useState(props)
return (
<div id="student-details" >
<h2>Name: {selectedStudent.student.name}</h2>
<h3>Age: {selectedStudent.student.age}</h3>
<h3>Class: {selectedStudent.student.class.name}</h3>
<h3>Test 1 Score: {selectedStudent.student.test1}</h3>
<EditStudent student={selectedStudent} setstudent={setStudent}/>
<DeleteStudent student={selectedStudent} setter={setStudent} />
</div>
)
}
export default StudentDetails;
Finally, here is the "EditStudent" component which is causing me so many problems (can't get the setStudent function from the parent to change the state)
import React, { useEffect, useState } from "react";
import { useMutation } from "#apollo/react-hooks";
//import { getStudents } from "../queries";
import StudentDetails from "./StudentDetails";
import { editStudentMutation, getStudentQuery, getStudents } from "../queries/index";
const EditStudent = ( setStudent ) => {
const { setStudent } = selectedStudent;
console.log(props)
const [name, setName] = useState();
const [age, setAge] = useState();
const [test, setTest] = useState();
const [editStudent] = useMutation(editStudentMutation);
return (
<form id="edit-student"
onSubmit={(e) => {
e.preventDefault();
editStudent({
variables: {
id: selectedStudent.student.student.id,
name: name,
age: age,
test1: test
},
refetchQueries: [{ query: getStudents}]
})
const aStudent = e.target.value;
setStudent(aStudent);
}}>
<div className="field" onChange={(e) => setName(e.target.value)}>
<label>Student Name:</label>
<input type="text"
value={name}/>
</div>
<div className="field" onChange={(e) => setAge(e.target.value)}>
<label>Age:</label>
<input type="text"
value={age}/>
</div>
<div className="field" onChange={(e) => setTest(e.target.value)}>
<label>Test One:</label>
<input type="text"
value={test}/>
</div>
<button type="submit" >submit</button>
</form>
)
}
export default EditStudent;
Your method named in your props setstudent "check left side of passed props"
<EditStudent student={selectedStudent} setstudent={setStudent}/>
and please access it like the following
const EditStudent = ( {setstudent} ) => {}
// or
const EditStudent = ( props ) => {
props.setstudent()
}
And these lines of code don't seem correct, from where you get this selectedStudent? your props named setStudent then you are accessing it to get the method setStudent
const EditStudent = ( setStudent ) => {
const { setStudent } = selectedStudent;
I'm a bit lost here. I've done this a bunch of time and have never had this issue before. I'm passing a boolean state to a modal component. I followed the code from the parent and it is set properly but as soon as it gets to the modal it returns as undefined.
Here is the parent:
import React, { useEffect, Fragment, useState } from 'react'
import './styles.css'
import LandingPageModal from './LandingPageModal'
import { testImages } from './testData'
const LandingPage = () => {
const [images, setImages] = useState([])
const [renderImages, setRenderImages] = useState(false)
const [showModal, setShowModal] = useState(false)
const [isLoaded, setIsLoaded] = useState(false)
useEffect(() => {
setImages(testImages)
setShowModal(true)
setIsLoaded(true)
}, [])
useEffect(() => {
if (images && images.length > 0) {
setRenderImages(true)
}
}, [images])
const FeaturedUsers = () => {
return (
renderImages ?
<Fragment>
<div className='grid'>
{images.map((image) => (
<img src={`/images/${image.src}`} alt={image.caption} />
))}
</div>
</Fragment> : ''
)
}
return(
isLoaded ?
<Fragment>
<FeaturedUsers />
<LandingPageModal show={showModal} />
</Fragment> : ''
)
}
export default LandingPage
and here is the modal:
import React, { useState, useEffect } from 'react'
import ReactModal from 'react-modal'
import './styles.css'
const LandingPageModal = ({ showModal }) => {
const [isModalOpen, setIsModalOpen] = useState(showModal)
console.log('Is Show: ' + showModal)
return (
<ReactModal
isOpen={isModalOpen}
>
<div className='main-wrapper'>
<div className='text'>
<p>
<strong>Welcome</strong>
<br />
<br />
Please sign in or sign up
</p>
</div>
</div>
</ReactModal>
)
}
export default LandingPageModal
In the LandingPage component, you accidentally renamed showModal to show.
I'm trying to done a form in react that have subcomponents for uploaded images (to do a preview and do it more beautyfull) but the thing is that I can't access to the useState of the child where is the image that I need to send to de backend.
Here is the code of the subcomponent and in the useState I need to acces throught the parent to the image:
import React, { useState, Fragment } from "react";
import {
Layout,
Container,
BoxUpload,
ContainerUploadImage,
TextUploadImage,
LabelUploadImage,
ImagePreview,
} from "./ImageUploadElements";
import UploadPhoto from "../../../images/upload.svg";
import CloseIcon from "../../../images/close.svg";
const ImageUpload = ({text}) => {
const [image, setImage] = useState("");
const [isUploaded, setIsUploaded] = useState(false);
const handleImageChange = (e) => {
if (e.target.files && e.target.files[0]) {
let reader = new FileReader();
reader.onload = (e) => {
setImage(e.target.result);
setIsUploaded(true);
};
reader.readAsDataURL(e.target.files[0]);
}
};
return (
<Layout>
<Container>
<h2>{text}</h2>
<BoxUpload>
<div className="image-upload">
{isUploaded ? (
<ImagePreview>
<img
className="close-icon"
src={CloseIcon}
alt="CloseIcon"
onClick={() => {
setIsUploaded(false);
setImage(null);
}}
/>
<img
src={image}
className="uploaded-image"
draggable={false}
alt="progress-uploaded"
/>
</ImagePreview>
) : (
<Fragment>
<LabelUploadImage htmlFor="upload-input">
<ContainerUploadImage
src={UploadPhoto}
alt="Upload Icon"
draggable={false}
/>
<TextUploadImage>Click to upload image</TextUploadImage>
</LabelUploadImage>
<input
type="file"
name="upload-input"
accept=".jpg,.jpeg,.gif,.png,.mov,.mp4"
onChange={handleImageChange}
/>
</Fragment>
)}
</div>
</BoxUpload>
</Container>
</Layout>
);
};
export default ImageUpload;
And here in that upload form component is where I need to get acces to this image to send it with axios to backend:
import React, { Fragment, useState } from "react";
import {
Container,
FormWrap,
FormContent,
Form,
FormH1,
FormLabel,
FormInput,
FormButton,
FormErrorWrap,
FormError,
FormErrorText,
PhotoWrap
} from "./UploadElements";
import ImageUpload from "../ImageUpload";
import { frontPhotoText, sidePhotoText, backPhotoText } from "./Data";
const Upload = () => {
const [weight, setWeight] = useState("");
const [uploadErrors, setUploadErrors] = useState([{}]);
const upload = (e) => {
e.preventDefault();
// Here will go the axios peticiĆ³n with the wight and the three images uploaded.
}
return (
<Fragment>
<Container>
<FormWrap>
<FormContent>
<Form onSubmit={upload}>
<FormH1>Upload New Progress</FormH1>
<FormLabel htmlFor="weight">Weight</FormLabel>
<FormInput
onChange={(e) => setWeight(e.target.value)}
type="number"
value={weight}
id="weight"
required
/>
<PhotoWrap>
<ImageUpload {...frontPhotoText}/>
<ImageUpload {...sidePhotoText}/>
<ImageUpload {...backPhotoText}/>
</PhotoWrap>
<FormErrorWrap>
{uploadErrors ? (
uploadErrors.map((err, index) => (
<FormError key={index}>
<FormErrorText>{err.msg}</FormErrorText>
</FormError>
))
) : (
<Fragment></Fragment>
)}
</FormErrorWrap>
<FormButton>Upload</FormButton>
</Form>
</FormContent>
</FormWrap>
</Container>
</Fragment>
);
};
export default Upload;
But I don't know how can I get this images throught the parent, if anyone can help I'll be very gratefull, thanks!!!
You can use a combination of forwardRef and useImperativeHandle to expose out a function from the child component that a parent component can invoke.
Child - Import and decorate the child component with forwardRef and use the useImperativeHandle to expose a getImage function that returns the current image state.
import React, { useState, Fragment, forwardRef } from "react";
...
const ImageUpload = forwardRef(({text}, ref) => {
const [image, setImage] = useState("");
const [isUploaded, setIsUploaded] = useState(false);
useImperativeHandle(ref, () => ({
getImage: () => image,
}));
const handleImageChange = (e) => {
...
};
return (
...
);
});
Parent - Create a React ref to pass to ImageUpload and in the callback access the current ref value and invoke the function.
import React, { Fragment, useState, useRef } from "react";
...
const Upload = () => {
const [weight, setWeight] = useState("");
const imageUploadFrontRef = useRef();
const imageUploadSideRef = useRef();
const imageUploadBackRef = useRef();
const [uploadErrors, setUploadErrors] = useState([{}]);
const upload = (e) => {
e.preventDefault();
const imageFront = imageUploadFrontRef.current.getImage();
const imageSide = imageUploadSideRef.current.getImage();
const imageBack = imageUploadBackRef.current.getImage();
// do with the images what you need.
}
return (
<Fragment>
<Container>
<FormWrap>
<FormContent>
<Form onSubmit={upload}>
...
<PhotoWrap>
<ImageUpload ref={imageUploadFrontRef} {...frontPhotoText} />
<ImageUpload ref={imageUploadSideRef} {...sidePhotoText} />
<ImageUpload ref={imageUploadBackRef} {...backPhotoText} />
</PhotoWrap>
...
</Form>
</FormContent>
</FormWrap>
</Container>
</Fragment>
);
};
I'm building app, using hooks and I got stuck.
I do not why but my Component does not download a state from my Context Component or maybe my initial state does not update correctly. Bellow I insert a few screenshot from my app.
Context Component:
import React, { createContext, useState } from 'react';
export const WeatherDataContext = createContext();
const WeatherDataContextProvider = (props) => {
const [weather, setWeather] = useState(
{
city: null,
temp: null
}
)
const addWeather = (city, temp) => {
setWeather({
city,
temp
})
}
return (
<WeatherDataContext.Provider value={{weather, addWeather}}>
{props.children}
</WeatherDataContext.Provider>
)
}
export default WeatherDataContextProvider
Form - axios - Component:
import React, {useContext, useState} from 'react';
import { WeatherDataContext } from '../context/WeatherDataContext';
import axios from 'axios'
import {Link} from 'react-router-dom'
const WeatherForm = () => {
const {addWeather} = useContext(WeatherDataContext);
const [value, setValue] = useState('')
const handleChange = (e) => {
e.preventDefault();
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${value}&appid=${KEY}&units=metric`)
.then(res => {
addWeather(res.data.name, res.data.main.temp)
})
}
return (
<div class='weather-form'>
<form onSubmit={handleChange}>
<input placeholder='City' onChange={(e) => setValue(e.target.value)} value={value} required/>
<Link to='/weather'><button>Search</button></Link>
</form>
</div>
)
}
export default WeatherForm
And final component where I want to use my update state
import React, {useContext, useState} from 'react';
import { WeatherDataContext } from '../context/WeatherDataContext';
const WeatherFront = () => {
const {weather} = useContext(WeatherDataContext)
console.log(weather)
return (
<div class='weather-front'>
<h1>City: {weather.city}, Temperatura: {weather.temp}</h1>
</div>
)
}
export default WeatherFront
The problem may be that you are not submitting the form.
<Link to='/weather'><button>Search</button></Link>
just navigates to WeatherFront.
You may try
import { useHistory } from "react-router-dom";
...
const WeatherForm = () => {
const history = useHistory()
const {addWeather} = useContext(WeatherDataContext)
const [value, setValue] = useState('')
const handleChange = (e) => {
e.preventDefault();
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${value}&appid=${KEY}&units=metric`)
.then(res => {
addWeather(res.data.name, res.data.main.temp)
history.push('/weather')
})
}
return (
<div class='weather-form'>
<form onSubmit={handleChange}>
<input placeholder='City' onChange={(e) => setValue(e.target.value)} value={value} required/>
<input type="submit" value="Search" />
</form>
</div>
)
}