I am creating a react app which is using local storage. I am saving and array of objects to local storage.
when I try to save to local storage the data is saving.
and then when I refresh the page the saved data is becoming empty object,
like this [].
if any one knows why its happening please help me
import React, {useEffect, useState} from 'react';
import Addcontact from './Addcontact';
import './App.css';
import Contactlist from './Contactlist';
import { Header } from './Header';
function App() {
const keyy ="contactlist"
const [contacts, setcontacts] = useState([])
const contactshandler = (contact)=> {
console.log(contact)
setcontacts([...contacts, contact])
}
useEffect(() => {
const getdata = JSON.parse(localStorage.getItem(keyy))
getdata && setcontacts(getdata)
}, [])
useEffect(() => {
localStorage.setItem(keyy, JSON.stringify(contacts));
}, [contacts])
return (
<div className="ui container">
<Header />
<Addcontact contacts={contacts} contactshandler={contactshandler} />
<Contactlist contacts={contacts} />
</div>
);
}
app component
import React, { useState } from 'react'
function Addcontact({contacts, setcontacts, contactshandler}) {
const [user, setuser] = useState({username:'', email:''})
const addvalue = (e) => {
e.preventDefault();
console.log(user)
contactshandler(user)
setuser({username:'', email:''})
}
return (
<div>
<div className='ui main'>
<h2> Add Contact</h2>
<form className='ui form' onSubmit={addvalue}>
<div className=''>
<label>name</label>
<input name="name" placeholder='name' value={user.username} onChange={(e) => setuser({...user, username : e.target.value })} />
</div>
<div className='feild'>
<label>email</label>
<input email='email' placeholder='email' value={user.email} onChange={(e) => setuser({...user, email: e.target.value})} />
</div>
<button>add</button>
</form>
</div>
</div>
)
}
export default Addcontact
export default App;
add component
this is the value showing when saving after refresh this value becomes empty object
enter image description here
console
enter image description here
You don't need useEffect to read the data. You can initially read it.
const [contacts, setcontacts] = useState(JSON.parse(localStorage.getItem(keyy)) ?? [])
and remove
useEffect(() => {
const getdata = JSON.parse(localStorage.getItem(keyy))
getdata && setcontacts(getdata)
}, [])
import React from "react";
export default function Form ({handleSubmit, handleChange, value}) {
return (
<form onSubmit = {handleSubmit}>
<label>
<input type="text" name="name" onChange = {handleChange}/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
I'm trying to create a basic ToDoList App. I'm getting stuck on the part in which you render the users input into my ToDoList Component. When I alert the value of e.target.value I get undefined. What is the problem? EDIT: Added my form component.
import "./styles.css";
import Header from "./Header.js";
import Form from "./Form.js";
import {useState} from "react";
import ToDoList from "./ToDoList.js"
export default function App() {
const[items, setItems] = useState([]);
let value = "";
function handleChange(e) {
value = e.target.value;
}
function handleSubmit(e) {
setItems([...items,
e.target.value
]);
alert('A name was submitted: ' + e.target.value);
e.preventDefault();
}
return (
<div className="App">
<div>
<Header/>
</div>
<div>
<Form handleChange = {handleChange} handleSubmit = {handleSubmit} value = {value}/>
</div>
<div>
<ToDoList items = {items}/>
</div>
</div>
);
}
Because you pass whole form as event to handleSubmit and items. If you want to keep this style of code, you must use input value changes. Also you must define e.preventDefault() before doing everything. So:
import "./styles.css";
import Header from "./Header.js";
import Form from "./Form.js";
import {useState} from "react";
import ToDoList from "./ToDoList.js"
export default function App() {
const[inputValue, setInputValue] = useState();
const[items, setItems] = useState([]);
let value = "";
function handleChange(e) {
setInputValue(e.target.value);
}
function handleSubmit(e) {
e.preventDefault();
setItems([...items,
inputValue
]);
setInputValue("");
alert('A name was submitted: ' + e.target.value);
}
return (
<div className="App">
<div>
<Header/>
</div>
<div>
<Form handleChange={handleChange} handleSubmit={handleSubmit} value = {value}/>
</div>
<div>
<ToDoList items = {items}/>
</div>
</div>
);
}
I have custom component which I am importing in my another Component as a Element tag. My custom Component consist of dropdown values. I want to read value the value of in my element tag when I submit my form
custom component :
import React, { useState, useMemo } from 'react'
import Select from 'react-select'
import countryList from 'react-select-country-list'
function CountrySelector() {
const [value, setValue] = useState('')
const options = useMemo(() => countryList().getData(), [])
const changeHandler = value => {
setValue(value)
}
return <Select options={options} value={value} onChange={changeHandler} />
}
export default CountrySelector
i want to use that custom component country selector values on my submit button:
main component:
import react from 'react';
import CountrySelector from '../../helpers/CountrySelector';
import IdType from '../../helpers/IdType';
import ProofOfAddress from '../../helpers/ProofOfAddress';
const submitForm=(e)=>{
//debugger;
e.preventDefault();
console.warn(e.target)
};
const IdentityVerification = (props) => {
const saveUser=(e)=>{
console.warn({e});
}
return (
<form onSubmit={submitForm} >
<div className='app'>
<label >Choose Issuing Country/region</label>
<CountrySelector/>
<label >Select ID Type</label>
<IdType/>
<label >Proof of Address</label>
<ProofOfAddress/>
</div>
<div className="form-actions">
<button >Submit</button>
</div>
</form>
);
};
export default IdentityVerification;
how can i read values?
The normal way to handle this would be to move the state and your changeHandler function into the parent component and pass the handler down to the child as a prop.
const IdentityVerification = (props) => {
const [value, setValue] = useState('')
const changeHandler = value => {
setValue(value)
}
return (
// ...
<CountrySelector onChange={changeHandler}/>
// ...
);
and in your child:
function CountrySelector({changeHandler}) {
// ....
return <Select options={options} value={value} onChange={changeHandler} />
}
I have a question on React Hooks. This is a sample of my code :-
import React, { useState, useEffect } from "react";
import Card from "./Card";
const CardsBoard = () => {
useEffect(() => {
doRatingClickProcessing()
}, [ratingObj])
const doRatingClickProcessing = () => {
const { player, title, rating } = ratingObj
}
return (
<div className="container-fluid justify-content-center">
<div className="row">
<div className="col-md-6">
<Card
cardInfo={player1Card}
player={1}
showCard={visiblePl1}
clickableRatings = {clickableRatings}
onClick={ratingObj => setRatingObj(ratingObj)}
/>
</div>
<div className="col-md-6">
<Card
cardInfo={player2Card}
player={2}
showCard={visiblePl2}
clickableRatings = {clickableRatings}
onClick={ratingObj => setRatingObj(ratingObj)}
/>
</div>
</div>
)}
</div>
)
}
export default CardsBoard
Then in the card component I am returning the ratingObj successfully when the user clicks on a rating.
In the Card Component I have something like this:-
<div
className="col-md-2 text-left card-rating-color"
onClick={() =>
onClick({
player: player,
title: row[0].title,
rating: row[0].rating,
})
}
>
{row[0].rating}
</div>
However I am puzzled why useEffect() is triggered even when the Card component is loaded, and ratingObj is still empty. Shouldn't it be triggered only if the ratingObj is filled up?
Thanks for your help and time
useEffect will call at least once. it doesn't matter either your object is updating or not because when you write
useEffect(()=>{
},[ratingObj]);
In above code you are passing object into square brackets right. That means you are mentioning dependencies as e second parameter and empty [] in argument list will call once at least. After that, it depends on ratingObj that you have passed in.
import React, {useState,useMemo} from 'react';
const App = () => {
const [name, setName] = useState('');
const [modifiedName, setModifiedName] = useState('');
const handleOnChange = (event) => {
setName(event.target.value);
}
const handleSubmit = () => {
setModifiedName(name);
}
const titleName = useMemo(()=>{
console.log('hola');
return `${modifiedName} is a Software Engineer`;
},[modifiedName]);
return (
<div>
<input type="text" value={name} onChange={handleOnChange} />
<button type="button" onClick={handleSubmit}>Submit</button>
<Title name={titleName} />
</div>
);
};
export default App;
const Title = ({name}) => {
return <h1>{name}</h1>
}
How can I pass the data from one React hooks form (component) to another component. For example if I need player name and photo pass from Profile.js and make it available in Navigation.js, how can i do that ?
Player.js
import React, { useContext , useEffect, useState } from "react";
import { useForm } from 'react-hook-form';
import { useHistory } from "react-router-dom";
import Axios from "axios";
import UserProfileContext from '../context';
const Profile = () => {
const { setData } = useContext(UserProfileContext);
const [email, setEmail] = useState('');
const [picture, setPicture] = useState('');
const [playerProfile, setPlayerProfile] = useState([]);
const loginUserEmail = localStorage.getItem('loginEmail');
const [updateProfile, setUpdateProfile] = useState({ _id: '', photo: '', name: '', email:'', phonenumber: '', position: '', password: '' })
const [isSent, setIsSent] = useState(false);
const [helperText, setHelperText] = useState('');
const [disabled, setDisabled] = useState(true);
const { handleSubmit, register, errors } = useForm();
const history = useHistory();
const onChangePicture = e => {
console.log('picture: ', picture);
if (e.target.files.length) {
setPicture(URL.createObjectURL(e.target.files[0]));
} else {
return false;
}
};
// If no profile image is being uploaded, to avoid the broken display of image, display a default image.
const addDefaultSrc = e => {
e.target.src = '/images/default-icon.png';
}
// Pass the id to the handler so you will know which item id changing.
const handleChange = (e, id) => {
e.persist();
let itemIndex;
const targetPlayer = playerProfile.find((player, index) => {
console.log({ player, id, index });
itemIndex = index; // Track the index so you can use it to update later.
return player.id === id;
});
console.log({ targetPlayer, id, e });
const editedTarget = {
...targetPlayer,
[e.target.name]: e.target.value
};
const tempPlayers = Array.from(playerProfile);
tempPlayers[itemIndex] = editedTarget;
/*
// Alternatively:: you can just map over the array if you dont want to track the index
const tempPlayers = playerProfile.map((profile, index) => {
return profile.id === id ? editedTarget : profile;
});
*/
setPlayerProfile(tempPlayers);
setUpdateProfile({ ...updateProfile, [e.target.name]: e.target.value }); // this is added just to see if its working
};
useEffect(() => {
const fetchData = async () => {
try {
const params = {
email: loginUserEmail,
};
const res = await Axios.get('http://localhost:8000/service/profile', {params});
setPlayerProfile(res.data.playerProfile);
} catch (e) {
console.log(e);
}
}
fetchData();
}, []);
const onSubmit = () => {
setDisabled(disabled);
const fetchData = async () => {
try {
const params = {
email: loginUserEmail,
};
const data = {photo: updateProfile.photo, name: updateProfile.name, email: updateProfile.email, phonenumber: updateProfile.phonenumber, position: updateProfile.position, password: updateProfile.password}
const res = await Axios.put('http://localhost:8000/service/profile', data, {params});
console.log("Front End update message:" + res.data.success);
if (res.data.success) {
setIsSent(true);
history.push('/')
}
else {
console.log(res.data.message);
setHelperText(res.data.message);
}
} catch (e) {
setHelperText(e.response.data.message);
}
}
fetchData();
}
return (
<div className="register_wrapper">
<div className="register_player_column_layout_one">
<div className="register_player_Twocolumn_layout_two">
<form onSubmit={handleSubmit(onSubmit)} className="myForm">
{
playerProfile.map(({ id, photo, name, email, phonenumber, position, privilege, password }) => (
<div key={id}>
<div className="formInstructionsDiv formElement">
<h2 className="formTitle">Profile</h2>
<div className="register_profile_image">
<input id="profilePic" name="photo" type="file" onChange={onChangePicture} />
</div>
<div className="previewProfilePic" >
<img alt="" onError={addDefaultSrc} name="previewImage" className="playerProfilePic_home_tile" src={photo} onChange={e => handleChange(e, id)}></img>
</div>
</div>
<div className="fillContentDiv formElement">
<label>
<input className="inputRequest formContentElement" name="name" type="text" value={name}
onChange={e => handleChange(e, id)}
maxLength={30}
ref={register({
required: "Full name is required",
pattern: {
value: /^[a-zA-Z\s]{3,30}$/,
message: "Full name should have minimum of 3 letters"
}
})}
/>
<span className="registerErrorTextFormat">{errors.name && errors.name.message}</span>
</label>
<label>
<input className="inputRequest formContentElement" name="email" type="text" value={email}
onChange={e => handleChange(e, id)}
disabled={disabled}
/>
</label>
<label>
<input className="inputRequest formContentElement" name="phonenumber" type="text" value={phonenumber}
onChange={e => handleChange(e, id)}
maxLength={11}
ref={register({
required: "Phone number is required",
pattern: {
value: /^[0-9\b]+$/,
message: "Invalid phone number"
}
})}
/>
<span className="registerErrorTextFormat">{errors.phonenumber && errors.phonenumber.message}</span>
</label>
<label>
<input className="inputRequest formContentElement" name="position" type="text" value={position}
onChange={e => handleChange(e, id)}
maxLength={30}
ref={register({
pattern: {
value: /^[a-zA-Z\s]{2,30}$/,
message: "Position should have minimum of 2 letters"
}
})}
/>
<span className="registerErrorTextFormat">{errors.position && errors.position.message}</span>
</label>
<label>
<div className="select" >
<select name="privilege" id="select" value={privilege} onChange={e => handleChange(e, id)}>
{/*<option selected disabled>Choose an option</option> */}
<option value="player">PLAYER</option>
{/*<option value="admin">ADMIN</option>*/}
</select>
</div>
</label>
<label>
<input className="inputRequest formContentElement" name="password" type="password" value={password}
onChange={e => handleChange(e, id)}
minLength={4}
maxLength={30}
ref={register({
required: "Password is required",
pattern: {
value: /^(?=.*?\d)(?=.*?[a-zA-Z])[a-zA-Z\d]+$/,
message: "Password begin with a letter and includes number !"
}
})}
/>
<span className="registerErrorTextFormat">{errors.password && errors.password.message}</span>
</label>
</div>
<label>
<span className="profileValidationText">{helperText}</span>
</label>
<div className="submitButtonDiv formElement">
<button type="submit" className="submitButton">Save</button>
</div>
</div>
))
}
</form>
</div>
</div>
</div>
);
}
Navigation.js
import React, { useContext } from 'react';
import { NavLink, useHistory } from 'react-router-dom';
import UserProfileContext from '../context';
const Navigation = () => {
const history = useHistory();
const { data } = useContext(UserProfileContext);
const divStyle = {
float:'left',
color: '#64cad8',
padding: '0px 0px 0px 10px',
font:'Lucida, sans-serif'
};
function logout() {
localStorage.removeItem('loginEmail')
localStorage.removeItem('Privilege')
history.push('/login')
window.location.reload(true);
}
return localStorage.getItem('loginEmail') &&
<div className="App">
<div className="wrapper">
<nav className="siteNavigation_nav_links">
<div className="clubLogo landing"style={divStyle}><b>Southside Soccer</b></div>
<NavLink className="mobile_register_link" to="/">Home</NavLink>
<NavLink className="mobile_register_link" to="/profile">Profile</NavLink>
<NavLink className="mobile_login_link" to="/login" onClick={logout}>Logout</NavLink>
<NavLink className="mobile_login_link" to='/aboutus'>About us</NavLink>
<div className="profileImage nav menu">
<span>{data.name}</span>|<img src=""></img>
</div>
</nav>
</div>
</div>
}
export default Navigation;
App.js
import React, { useState } from 'react';
import "./App.css";
import "./CSSModules/home.css";
import "./CSSModules/register.css";
import "./CSSModules/login.css";
import "./CSSModules/aboutus.css";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./components/Home";
import Register from "./components/Register";
import Login from "./components/Login";
import Aboutus from "./components/Aboutus";
import Navigation from "./components/Navigation";
import Profile from "./components/Profile";
import { ProtectedRoute } from "./components/protected.route";
import UserProfileContext from './context';
var ReactDOM = require("react-dom");
const App = () => {
const [data, setData] = useState({
id: '',
name: '',
email: '',
photo: '',
});
return (
<UserProfileContext.Provider value={{ data, setData }}>
<BrowserRouter>
<>
<Navigation />
<Switch>
<ProtectedRoute exact path="/" component={Home} />
<ProtectedRoute path="/profile" component={Profile} />
<ProtectedRoute path="/aboutus" component={Aboutus} />
<Route path="/register" component={Register} />
<Route path="/login" component={Login} />
</Switch>
</>
</BrowserRouter>
</UserProfileContext.Provider>
);
};
ReactDOM.render(
React.createElement(App, null),
document.getElementById("root")
);
export default App;
context.js
import React from 'react';
export default React.createContext();
Like mentioned in the comments, one option is to left your application's state up (and that should be the preferred option for simple state).
In practice, that would look like:
App.js
import React, { useState } from 'react';
import Navigation from './Navigation';
import Profile from './Profile';
function App() {
const [name, setName] = useState('');
return (
<div className="App">
<Navigation name={name} />
<hr />
<Profile name={name} setName={setName} />
</div>
);
}
export default App;
Profile.js:
import React from 'react';
const Profile = ({ name, setName }) => {
return (
<>
<div>Profile: {name}</div>
<input
type="text"
name="name"
value={name}
onChange={e => setName(e.target.value)}
/>
</>
);
};
export default Profile;
Navigation.js:
import React from 'react';
const Navigation = ({ name }) => {
return <div>Navigation: {name}</div>;
};
export default Navigation;
Edit: After a closer look at your code, I think using context API makes more sense in this case.
Try the following:
context.js
import React from 'react';
export default React.createContext();
App.js
import React, { useState } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import './styles.css';
import Profile from './components/Profile';
import Navigation from './components/Navigation';
import UserProfileContext from './context';
const App = () => {
const [data, setData] = useState({
id: 'player-1',
name: 'Player One',
age: 25,
photo: 'rose.JPG',
});
return (
<UserProfileContext.Provider value={{ data, setData }}>
<BrowserRouter>
<Navigation />
<Switch>
<Route path="/profile" component={Profile} />
</Switch>
</BrowserRouter>
</UserProfileContext.Provider>
);
};
export default App;
components/Navigation.js
import React, { useContext } from 'react';
import { NavLink } from 'react-router-dom';
import UserProfileContext from '../context';
const Navigation = () => {
const { data } = useContext(UserProfileContext);
const divStyle = {
float: 'left',
color: '#64cad8',
padding: '0px 0px 0px 10px',
font: 'Lucida, sans-serif',
};
return (
<div className="App">
<div className="wrapper">
<nav className="siteNavigation_nav_links">
<div className="clubLogo landing" style={divStyle}>
<b>Soccer</b>
</div>
<NavLink className="mobile_register_link" to="/profile">
Profile
</NavLink>
<div className="profileImage nav menu">
<span>{data.name}</span> | <img alt="" src={data.photo} />
</div>
</nav>
</div>
</div>
);
};
export default Navigation;
components/Profile.js
import React, { useContext } from 'react';
import { useForm } from 'react-hook-form';
import UserProfileContext from '../context';
const Profile = () => {
const { setData } = useContext(UserProfileContext);
const { register, handleSubmit } = useForm();
return (
<div>
<form onSubmit={handleSubmit(setData)}>
<b>Profile</b>
<input name="id" ref={register} />
<input name="name" ref={register} />
<input name="age" ref={register} />
<button type="submit" className="submitButton">
Click
</button>
</form>
</div>
);
};
export default Profile;
You can utilize react-redux for global state handling or pass a callback function from Navigation.js to Profile.js.
Use React's built in context API. Wrap your App in context provider, then you can use useContext hook to access state, dispatch state updates between components.
App level setup - one time
// Define Context
const AppContext = React.createContext()
// Define initial state
const initialState = {}
// Define Reducer
const Reducer = (state, dispatch) => {
switch(action.type) {
case "ACTION_TYPE": return {...state, prop: action.payload}
default: return state
}
}
//Wrap main App in context, pass state from React's useReducer hook
const [state, dispatch] = useReducer(Reducer, initialState)
<AppContext.Provider data={{
state,
dispatch
}}>
<ReactAppMainWrapper />
</AppContext.Provider>
Component level
const {state, dispatch} = useContext(AppContext);
// to update state call dispatch from anywhere, all components consuming state will be updated
dispatch({
type: "ACTION_TYPE",
payload: "new_value"
})
Explanation
The state serves as redux like store, available in the entire app.
In any component, import AppContext and use React's builtin useContext hook to interact with store from the component.
Data object passed in Context provider above is available from this.