React Hooks - Context - reactjs

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>
)
}

Related

Using the useContext Hook to create a simple To-Do List app in React

Can someone tell me if there is anything wrong with the way I have used the Context API in this code. And if there is something wrong can you explain why?
These two are my Contexts
import React from "react";
export const ItemListContext = React.createContext();
import React from "react";
export const ItemContext = React.createContext();
This is my App component
import "./styles.css";
import TodoList from "./Components/TodoList";
import { ItemContext } from "./Context/ItemContext";
import { ItemListContext } from "./Context/ItemListContext";
import { useState } from "react";
export default function App() {
const [inputs, setInput] = useState("");
const [itemList, setItemList] = useState([]);
return (
<div className="App">
<ItemContext.Provider value={[inputs, setInput]}>
<ItemListContext.Provider value={[itemList, setItemList]}>
<TodoList />
</ItemListContext.Provider>
</ItemContext.Provider>
</div>
);
}
After this I have a Todo List component that looks like this :-
import React, { useContext, useState } from "react";
import { ItemContext } from "../Context/ItemContext";
import { ItemListContext } from "../Context/ItemListContext";
const TodoList = () => {
const [input, setInput] = useContext(ItemContext);
const [itemList, setItemList] = useContext(ItemListContext);
const handleChange = (e) => {
setInput(e.target.value);
};
const hanleCLick = () => {
setItemList((prevList) => [...prevList, input]);
};
const handleDelete = (i) => {
let newList = itemList.filter((item, index) => index !== i);
setItemList(newList);
};
return (
<>
<input type="text" value={input} onChange={handleChange} />
<button onClick={hanleCLick}>Add Item</button>
{itemList.map((item, index) => {
return (
<div key={index}>
<p>{item}</p>
<button onClick={() => handleDelete(index)}>Delete</button>
</div>
);
})}
</>
);
};
export default TodoList;

Using navigate react-router v6 in statement

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;

How to configure React-Context

So I have a context.
import React from "react";
const CartContext = React.createContext({
numberOfMeals: 0,
meals: [],
});
export default CartContext;
I then import this into my App.JS
import React from "react";
import MealsBase from "./Components/Meals/MealsBase";
import CartContext from "./Context/CartContext";
function App() {
return (
<CartContext.Provider value={{ numberOfMeals: 0, meals: [] }}>
<MealsBase/>
</CartContext.Provider>
);
}
export default App;
I use this context within two components.
Component 1 :
import React, {useContext} from "react";
import "./CartIcon.css";
import CartContext from "../../Context/CartContext";
const CartIcon = () => {
const cartContext = useContext(CartContext);
return (
<React.Fragment>
<button className="border">
<h3>Your Cart : {cartContext.numberOfMeals}</h3>
</button>
</React.Fragment>
);
};
export default CartIcon;
Component 2 :
import React,{ useState, useContext } from "react";
import CartContext from "../../Context/CartContext";
const MealForm = () => {
const [amount, setAmount] = useState(0);
const mealContext = useContext(CartContext);
const amountHandler = (event) => {
setAmount(event.target.valueAsNumber);
};
const formHandler = (event) => {
mealContext.numberOfMeals = mealContext.numberOfMeals + amount;
console.log(mealContext.numberOfMeals);
event.preventDefault();
}
return(
<React.Fragment>
<form onSubmit={formHandler}>
<label>Amount</label>
<input type="number" step={1} min={-1} max={50} value={amount} onChange={amountHandler}/>
<button type="submit"> + Add </button>
</form>
</React.Fragment>
);
};
export default MealForm
The context seems to update in component two but not in component one.
I have an understanding that if useContexts values change then it causes a component to re-render. So I am struggling to understand why the CartIcon.js file is not being re-rendered.
You aren't really updating the value the React way, you are mutating it. Move the context value into state and provide that out.
const CartContext = React.createContext({
numberOfMeals: 0,
setNumberOfMeals: () => {},
meals: [],
setMeals: () => {},
});
...
function App() {
const [numberOfMeals, setNumberOfMeals] = useState(0);
const [meals, setMeals] = useState([]);
return (
<CartContext.Provider value={{ numberOfMeals, setNumberOfMeals, meals, setMeals }}>
<MealsBase/>
</CartContext.Provider>
);
}
...
const MealForm = () => {
const [amount, setAmount] = useState(0);
const { setNumberOfMeals } = useContext(CartContext);
const amountHandler = (event) => {
setAmount(event.target.valueAsNumber);
};
const formHandler = (event) => {
event.preventDefault();
setNumberOfMeals(count => count + amount);
}
return(
<React.Fragment>
<form onSubmit={formHandler}>
<label>Amount</label>
<input type="number" step={1} min={-1} max={50} value={amount} onChange={amountHandler}/>
<button type="submit"> + Add </button>
</form>
</React.Fragment>
);
};

React Lifting State: Updating state in the child using setState function from parent

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;

React - Using state in component by useContext

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>

Resources