Back functionality of browser is not working - reactjs

I have written the following code with a Query string corresponding to the filters applied. On clicking browser's back button I want to get back to the previous set of filters, but instead I land back to the browser's home page every time.
import React, {useState, useEffect, useCallback} from 'react'
import {DropDownList, MultiSelect} from "#progress/kendo-react-dropdowns"
import {Button} from '#progress/kendo-react-buttons'
import { useHistory, useLocation } from "react-router-dom"
const Filter = () => {
const history=useHistory()
const location=useLocation()
const [Standard,setStandard] = useState("All")
const [Hobbies,setHobbies] = useState(["Playing"])
const [Responsibility,setResponsibility] = useState("All")
const [QueryString,setQueryString] = useState(location.search.substring(1))
const options={
StandardList:["All","VI","VII","VIII"],
HobbiesList: ["Playing", "Drawing","Swimming"],
ResponsibilityList:["All","Monitor","Head","Sports Captain"]
}
const handleApply = ()=>{
setQueryString(`Standard=${JSON.stringify(Standard)}&Responsibility=${JSON.stringify(Responsibility)}&IncidentStatus=${JSON.stringify(Hobbies)}`)
}
const backButtonEvent = useCallback((e) => {
e.preventDefault();
history.goBack()
},[])
useEffect(() => {
window.addEventListener('popstate',backButtonEvent);
return ()=>{
window.removeEventListener('popstate',backButtonEvent);
}
},[backButtonEvent])
useEffect(() => {
var params= new URLSearchParams((QueryString)?(QueryString):`Standard=${JSON.stringify(Standard)}&Responsibility=${JSON.stringify(Responsibility)}&IncidentStatus=${JSON.stringify(Hobbies)}`)
history.push({search: params.toString()})
}, [QueryString])
return (
<div>
<label>Standard </label>
<DropDownList data={options.StandardList} defaultValue={"All"} value={Standard}
onChange={(event)=>setStandard(event.target.value)}/>
<label > Hobbies </label>
<MultiSelect data={options.HobbiesList} defaultValue={["Playing"]} value={Hobbies} onChange={(event)=>setHobbies([...event.value])}/>
<label > Responsibility </label>
<DropDownList data= {options.ResponsibilityList} defaultValue= {"All"} value={Responsibility} onChange={(event)=>setResponsibility(event.target.value)} />
<Button id="submitFilter" type="button" onClick={handleApply} > Apply </Button>
</div>
)
}
export default Filter
Why am I getting this behaviour and how can I resolve it?

Related

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;

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;

Why am I getting a null value in localStorage even after parsing it?

I'm trying to build a crypto tracker where you can add the items by clicking a button. Each time the button is clicked, the array should be added to the storage with the name "crypto" and then on another component where it is the portfolio one we should be able to get the items.
Here is where I set the item to an array whenever I click the add button:
import React, {useEffect, useState} from 'react'
import axios from 'axios'
import './tracker.css'
import Navigation from './Nav.js'
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
function Tracker() {
const [data, setData] = useState([])
const [portfolio, setPortfolio] = useState([])
useEffect(() => {
setInterval(() => {
const fetchData = async () => {
const result = await axios('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false' , {
'mode': 'no-cors',
'headers': {
'Access-Control-Allow-Origin': '*',
}
})
setData(result.data)
}
fetchData()
}, 1000)
}, [])
return (
<div>
<Navigation />
<div className="tracker__names">
<b>Coins</b>
<b>Symbol</b>
<b>Price</b>
<b>Market Cap</b>
</div>
{data.map((coins, i) => {
const addToPortfolio = () => {
setPortfolio([...portfolio, data[i]])
localStorage.setItem('crpyto', JSON.stringify(portfolio))
}
return (
<>
<div className="tracker__main">
<div className="tracker__img">
<img src={coins.image} className="tracker__image"/>
<button key={i} onClick={addToPortfolio}>{coins.id}</button>
</div>
<div className="tracker__symbol">
<p>{coins.symbol}</p>
</div>
<div className="tracker__price">
<p></p>
${coins.current_price}
</div>
<div className="tracker__market">
<p></p>
${coins.market_cap}
</div>
</div>
</>
)
})}
</div>
)
}
export default Tracker
Here is the component where I want to get the item:
import React, {useState, useEffect} from 'react'
import Navigation from './Nav.js'
function Portfolio() {
const [value, setValue] = useState(JSON.parse(localStorage.getItem('crypto')) || '')
useEffect(() => {
console.log(value)
}, )
return (
<div>
<Navigation />
{value}
</div>
)
}
export default Portfolio
It is because useState is executed before JSON.parse(localStorage.getItem('crypto')) and once you get the value from the localstorage, component doesn't re-render.
Instead do:
useEffect(() => {
const crypto = JSON.parse(localStorage.getItem('crypto'))
if(crypto) setValue(crypto)
}, [])
In React you can't set a state var and on the next line save it in localStorage (or even read it). This because setPortfolio is async!
To solve this you have I think 2 ways:
store value and not state variable:
localStorage.setItem('crpyto', JSON.stringify([...portfolio, data[i]]))
use an useEffect hook:
useEffect(() => {
localStorage.setItem('crpyto', JSON.stringify(portfolio))
}, [portfolio])
First of all, when yo uare setting state like this, in the next block of code, portfolio won't necessarily have the updated state.
setPortfolio([...portfolio, data[i]])
localStorage.setItem('crpyto', JSON.stringify(portfolio))
update the portfolio like this.
const newPortfolio = [...portfolio, data[i]];
setPortfolio(newPortfolio )
localStorage.setItem('crpyto', JSON.stringify(newPortfolio))

React useState hook - I want to render certain component when only press submit button not when onChange

I'm new to react.
Now trying to make form with react hooks, and I want to render Cloud component only when press submit button. But it rendered every onChange called.
I know that onChange re-rendered cause also useState hook.
But have no idea how to render only when press submit button.
My final goal is when write name and press enter, if value is not contained in api, setShake make shake True and if True, put shake-cloud class in Cloud.js.
REACT IS TOO DIFFICULT :(
Thanks for help tho :)
App.js
import React, { useState, useEffect } from "react";
import "./App.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import "./search.css";
import PageTitle from "./component/PageTitle";
import Cloud from "./component/Cloud";
import Loading from "./component/Loading";
//https://api.color.pizza/v1/
//data.colors[0].name
const App = () => {
const [isLoading, setIsLoading] = useState(false);
const [colorNames, setColorNames] = useState("");
const [search, setSearch] = useState("");
const [query, setQuery] = useState("");
const [cloudHex, setCloudHex] = useState("ivory");
const [shake, setShake] = useState(false);
useEffect(() => {
getColorLists();
}, []);
const getColorLists = async () => {
const res = await fetch(`https://api.color.pizza/v1/`);
const data = await res.json();
await setColorNames(data);
setIsLoading(true);
};
const isColor = () => {
let makeUpper =
query.search(/\s/) == -1
? query.charAt(0).toUpperCase() + query.slice(1)
: query
.split(" ")
.map((i) => i.charAt(0).toUpperCase() + i.slice(1))
.join(" ");
for (let i = 0; i < colorNames.colors.length; i++) {
if (colorNames.colors[i].name == makeUpper) {
setCloudHex(colorNames.colors[i].hex);
return;
} else if (i == colorNames.colors.length - 1) {
return makeShake();
}
}
};
const updateSearch = (e) => {
setSearch(e.target.value);
};
const getSearch = (e) => {
e.preventDefault();
setQuery(search);
isColor();
};
const makeShake = async () => {
await setShake(true)
await setShake(false)
}
return (
<>
{!isLoading ? (
<Loading />
) : (
<div className="App">
<div className="app-wrap">
<PageTitle />
<div className="search-wrap">
<form onSubmit={getSearch} className="search-form">
<input
className="search-bar"
type="text"
value={search}
onChange={updateSearch}
/>
<button type="submit" className="search-button">
<FontAwesomeIcon
icon={faSearch}
className="search"
/>
</button>
</form>
</div>
<Cloud cloudhex={cloudHex} shake={shake} />
</div>
</div>
)}
</>
);
};
export default App;
Cloud.js
import React, {useEffect} from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faCloud } from "#fortawesome/free-solid-svg-icons";
import './cloud.css';
const Cloud = ({cloudhex, shake}) => {
useEffect(() => {
}, [])
console.log(shake)
return (
<div className={`cloud-wrap ${ shake ? "shake-cloud":''}`}>
<span className="cloudhexname">{cloudhex}</span>
<FontAwesomeIcon icon={faCloud} className="cloud" style={{color:`${cloudhex}`}} />
</div>
);
};
export default Cloud;
A good approach in this case is to use useRef() Hook to store our search field value, instead of using useState(). Because useRef() Hook does not force a re-render while useState() does. This approach is known as un-controlled way to use input field.
You basically need to make few modifications in your code which are as follows:
const search = useRef("");
Then remove onChange={updateSearch} and value={search} from input and use a property ref={search}. So that your input looks like below:
<input
className="search-bar"
type="text"
ref={search}
/>
Then in the submit handler, you can get the value of the input field using search.current.value. So your getSearch() would look like
const getSearch = (e) => {
e.preventDefault();
setClicked(true);
setQuery(search.current.value);
isColor();
};
Assuming user has typed an input. If not then you can set a validation before using setQuery() in the getSearch() form submit handler.
if(search.current.value){
setQuery();
}
Note: If you have any other controlled inputs in your project, you can change then to un-controlled inputs using refs and this way re-renders would not happen in your code.
Do it like that
If you want to render cloud component after form submit then put one flag and toggle that, here I take clicked state
import React, { useState, useEffect } from "react";
import "./App.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import "./search.css";
import PageTitle from "./component/PageTitle";
import Cloud from "./component/Cloud";
import Loading from "./component/Loading";
//https://api.color.pizza/v1/
//data.colors[0].name
const App = () => {
const [isLoading, setIsLoading] = useState(false);
const [colorNames, setColorNames] = useState("");
const [search, setSearch] = useState("");
const [query, setQuery] = useState("");
const [cloudHex, setCloudHex] = useState("ivory");
const [shake, setShake] = useState(false);
const [clicked, setClicked] = useState(false);
useEffect(() => {
getColorLists();
}, []);
const getColorLists = async () => {
const res = await fetch(`https://api.color.pizza/v1/`);
const data = await res.json();
await setColorNames(data);
setIsLoading(true);
};
const isColor = () => {
let makeUpper =
query.search(/\s/) == -1
? query.charAt(0).toUpperCase() + query.slice(1)
: query
.split(" ")
.map((i) => i.charAt(0).toUpperCase() + i.slice(1))
.join(" ");
for (let i = 0; i < colorNames.colors.length; i++) {
if (colorNames.colors[i].name == makeUpper) {
setCloudHex(colorNames.colors[i].hex);
return;
} else if (i == colorNames.colors.length - 1) {
return makeShake();
}
}
};
const updateSearch = (e) => {
setSearch(e.target.value);
};
const getSearch = (e) => {
e.preventDefault();
setClicked(true);
setQuery(search);
isColor();
};
const makeShake = async () => {
await setShake(true)
await setShake(false)
}
return (
<>
{!isLoading ? (
<Loading />
) : (
<div className="App">
<div className="app-wrap">
<PageTitle />
<div className="search-wrap">
<form onSubmit={getSearch} className="search-form">
<input
className="search-bar"
type="text"
value={search}
onChange={updateSearch}
/>
<button type="submit" className="search-button">
<FontAwesomeIcon
icon={faSearch}
className="search"
/>
</button>
</form>
</div>
{clicked && <Cloud cloudhex={cloudHex} shake={shake} />}
</div>
</div>
)}
</>
);
};
export default App;

child/parent problem rendering simple bar chart in react,

"EDITED"
I'm trying to make a very minimalist bar chart.
It doesn't, render. It seems that the child component isn't rendering after the parent component's state has changed.
for example. if I make a change in my code in the MiniChart component, and save the changed through my IDE. the charts will render correctly. but as soon as navigate to another page in my app through my browser and come back to where the charts are, then they won't render.
Any help will be much appreciated.
Child component:
import React, {useState, useEffect} from 'react';
import axios from 'axios';
import {BarChart, Bar} from 'recharts';
const MiniChart = (props) => {
const [apiUrl] = useState("https://api.coingecko.com/api/v3/coins/"+props.id+"/market_chart?vs_currency=usd&days=30&interval=daily");
const [data, setData] = useState([]);
const [msg, setMsg] = useState([]);
const [r, setR] = useState([]);
// fetch data from api
useEffect(() => {
const fetchData = async () => {
if(parseInt(props.rank) < 5){
const result = await axios(apiUrl,);
setData(result.data.prices);
} else {setMsg("TEST : not loaded");}
}
setR(data.map(elem => ({ 'val': elem[1]})));
fetchData();
return()=>{
}
}, [apiUrl, props.rank]);
return (
<div>
<BarChart width={150} height={40} data={r}>
<Bar dataKey='val' fill="green" />
</BarChart>
</div>
)
}
export default MiniChart
Parent component:
import React, { useState} from 'react'
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faStar } from "#fortawesome/free-solid-svg-icons";
import { Link, useLocation } from 'react-router-dom';
import Cookies from 'universal-cookie';
import MiniChart from './MiniChart';
const Crypto = (props) => {
const location = useLocation();
const [starColor, setStarColor] = useState(props.defaultStarCol);
const cookies = new Cookies();
const getFavs = cookies.getAll();
// toggle color, re-render, remove or add to cookies
const handleFavToggle = (e) => {
if(starColor === '#ebc934'){
setStarColor('lightgrey');
cookies.remove(props.id, props.id, { path: '/' });
if(location.pathname === '/favorites'){
function refreshPage() {
window.location.reload(false);
}
refreshPage();
}
} else {
setStarColor('#ebc934');
cookies.set(props.id, props.id, { path: '/' });
//console.log(cookies.getAll());
}
}
return (
<div>
<li>
<div className="lidiv">
{props.id in getFavs? //ADD IF LOGGED IN !
<p className="pml"><FontAwesomeIcon style={{color:'#ebc934'}} onClick={handleFavToggle} className="star" icon={faStar}/></p>
: <p className="pml"><FontAwesomeIcon style={{color:'lightgrey'}} onClick={handleFavToggle} className="star" icon={faStar}/></p>}
<p className="pml">{props.rank}</p>
<img className="iconimg" src={props.img} alt=""/>
<p className="pxl coinName"><Link to="/crypto" style={{display: 'block'}}>{props.coin}</Link></p>
<p className="pml">{props.tag}</p>
<p className="pml4">{props.price}</p>
<p className="pml" style={{color: (props.oneday).charAt(0)==='-' ? 'red': 'green'}}>{props.oneday}%</p>
<p className="pxl daycash" style={{color: (props.oneday).charAt(0)==='-' ? 'red': 'green'}}>{props.onedaycurr} </p>
<p className="pxl-4">{props.mcap}M</p>
<MiniChart className="pxl" id={props.id} rank={props.rank}></MiniChart>
</div>
</li>
</div>
)
}
export default Crypto;

Resources