React Hook useEffect has a missing dependency 'formdata' - reactjs

Keep having a warning missing dependency in my form,
i tried input the formdata into the array of useEffect, but if i input the formdata, it will show Maximum update depth exceeded
Full warning : "React Hook useEffect has a missing dependency : 'formdata'. Either include it or remove the dependency array"
And my code is:
const [isRevealPassword, isRevealPasswordHandler] = useState(false);
const [formdata, formdataHandling] = useState({
email: {
value: '',
config: {
type: 'email'
}
},
password: {
value: '',
config: {
type: 'password'
}
}
});
useEffect(() => {
const newFormdata = {
...formdata
}
const newElement = {
...newFormdata['password']
}
newElement.config.type = isRevealPassword ? 'text' : 'password';
newFormdata['password'] = newElement;
formdataHandling(newFormdata);
}, [isRevealPassword]) // <--- if I input formdata here, it will looping until Maximum update depth exceeded
const togglePassword = () => {
isRevealPasswordHandler(!isRevealPassword);
}
return (
<span
onClick={() => togglePassword()}
>
{
isRevealPassword ? <FaEye /> : <FaEyeSlash />
}
</span>
)
the answer is in the comment below, all credits goes to mr #Yousaf ( will delete this message here, if mr Yousaf himself post the answer )

Related

setState (array) entries are disappearing

In the following example, User2's messages are getting erased from the state as soon as the User1 sends a message.
User1 sends a message, it gets displayed on screen. As soon as User2 replies, User1's messages disappear.
I'm persuaded it's a React setState mistake but I've followed the react-native-chat-ui's docs as much as possible but somehow there is something going wrong and I cannot put my finger on it.
Here's a video of the bug in action: https://streamable.com/rxbx18
Thank you.
import React, { useEffect, useState } from 'react';
import { Chat, MessageType } from '#flyerhq/react-native-chat-ui'
import { SafeAreaProvider } from 'react-native-safe-area-context'
const uuidv4 = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.floor(Math.random() * 16)
const v = c === 'x' ? r : (r % 4) + 8
return v.toString(16)
})
};
const user = { id: uuidv4(), firstName: 'User1' };
const chatbot = { id: uuidv4(), firstName: 'User2' };
const App = () => {
const [messages, setMessages] = useState<MessageType.Any[]>([])
const addMessage = (message: MessageType.Any) => {
setMessages([message, ...messages]);
};
const handleSendPress = (message: MessageType.PartialText) => {
// display user message
const textMessage: MessageType.Text = {
author : user,
createdAt: Date.now(),
id : uuidv4(),
text : message.text,
type : 'text',
};
addMessage(textMessage);
// display bot message
// NOTE: adding a timeout so that you can see user's message for a second...
setTimeout(() => {
const chatbotTextMessage: MessageType.Text = {
author : chatbot,
createdAt: Date.now(),
id : uuidv4(),
text : `Response that will erase user's messages...`,
type : 'text',
};
addMessage(chatbotTextMessage);
}, 1000);
};
return (
<SafeAreaProvider>
<Chat
messages={messages}
showUserNames={true}
onSendPress={handleSendPress}
user={user}
/>
</SafeAreaProvider>
);
}
export default App;

How to get state variable names from names(string) value in React Native?

1. I set name in statesCorelatedFields and setStatesCorelatedFields inside below codes,
how can I get state and setState variables from there? (please see below example)
2. Does my below approach right?
3. Any suggestion will be highly appreciated.
I am using react native 0.68.5.
Previously, I used class component, now I am migrating to function component.
I have a reuseable file and App file like below:
reuseable.js
// import ...
export const handleFocus = (
state,
setState,
focusStyle,
// array of state variables of corelated fields
statesCorelatedFields,
// array of setState methods of corelated fields
setStatesCorelatedFields,
// blur style if no text value
blurNoTextStyle,
) => {
const stateData = { ...state };
stateData.styleName = { ...focusStyle };
// for corelated fields: empty value and set blurNoTextStyle
if (statesCorelatedFields.length) {
let stateCorelatedFieldData;
for (i = 0; i < statesCorelatedFields.length; i++) {
stateCorelatedFieldData = { ...statesCorelatedFields[i] };
stateCorelatedFieldData.value = '';
stateCorelatedFieldData.styleName = { ...blurNoTextStyle };
setStatesCorelatedFields[i](stateCorelatedFieldData);
}
}
setState(stateData);
};
// export const handleChangeText=(state, setState, text, ...)=>{...}
// export const handleBlur=(state, setState, ...)=>{...}
// ...
App.js
// import ...
// import all methods from reuseable.js
const App = () => {
const [email, setEmail] = useState({
name: 'email',
value: '',
styleName: { ...styles.blurNoTextStyle },
error: '',
statesCorelatedFields: [],
setStatesCorelatedFields: [],
});
const [countryCode, setCountryCode] = useState({
name: 'countryCode',
value: '',
styleName: { ...styles.blurNoTextStyle },
error: '',
// I set name here; how can I get state and setState variable from here
statesCorelatedFields: ['phoneNumber'],
setStatesCorelatedFields: ['setPhoneNumber'],
});
const [phoneNumber, setPhoneNumber] = useState({
name: 'phoneNumber',
value: '',
styleName: { ...styles.blurNoTextStyle },
error: '',
statesCorelatedFields: [],
setStatesCorelatedFields: [],
});
return (
<>
{/* components */}
<TextInput
value={countryCode.value}
onChangeText={(text) => handleChangeText(countryCode, setCountryCode, text)}
onFocus={() => handleFocus(countryCode, setCountryCode, styles.focusStyle, countryCode.statesCorelatedFields, countryCode.setStatesCorelatedFields)}
onBlur={() => handleBlur(countryCode, setCountryCode)}
/>
{/* other components */}
</>
);
}
const styles = StyleSheet.create({
// styles goes here
});
export default App;
Thanks in advance.
Moves this setStatesCorelatedFields out of the loop body you are updating the state on every iteration which doesn't need. It causes to slow down your component
you can do like this:
if (statesCorelatedFields.length) {
let stateCorelatedFieldData;
for (i = 0; i < statesCorelatedFields.length; i++) {
stateCorelatedFieldData = { ...statesCorelatedFields[i] };
stateCorelatedFieldData.value = "";
stateCorelatedFieldData.styleName = { ...blurNoTextStyle };
}
setStatesCorelatedFields[i](stateCorelatedFieldData);
}

prevent api from being called before state is updated

I have a list of objects. I want to make an api call once the location field of the object is changed. So for that, I have a useEffect that has id, index and location as its dependencies. I have set a null check for the location, if the location isn't empty, I want to make the api call. But the thing is, the api is being called even when the location is empty, and I end up getting a 400. How can I fix this and make the call once location isn't empty?
const [plants, setPlants] = useState([
{
name: 'Plant 1',
id: uuidv4(),
location: '',
coords: {},
country: '',
isOffshore: false,
}
]);
const [locationIDObject, setlocationIDObject] = useState({
id: plants[0].id,
index: 0
});
const handlePlantLocChange = (id, index, value) => {
setPlants(
plants.map(plant =>
plant.id === id
? {...plant, location: value}
: plant
)
)
setlocationIDObject({
id: id,
index: index
})
}
const getCoords = (id, index) => {
axios.get('http://localhost:3002/api/coords', {
params: {
locAddress: plants[index].location
}
}).then((response) => {
if(response.status === 200) {
handlePlantInfoChange(id, PlantInfo.COORD, response.data)
}
})
}
const handler = useCallback(debounce(getCoords, 5000), []);
useDeepCompareEffect(() => {
if(plants[locationIDObject.index].location !== '')
handler(locationIDObject.id, locationIDObject.index);
}, [ locationIDObject, plants[locationIDObject.index].location])
return (
<div className='plant-inps-wrapper'>
{
plants.map((item, idx) => {
return (
<div key={item.id} className="org-input-wrapper">
<input placeholder={`${item.isOffshore ? 'Offshore' : 'Plant ' + (idx+1) + ' location'}`} onChange={(e) => handlePlantLocChange(item.id, idx, e.target.value)} value={item.isOffshore ? 'Offshore' : item.location} className="org-input smaller-input"/>
</div>
)
})
}
</div>
)
I think your useCallback is not updating when value of your variables is changing and that is the issue:
Although the check is correct, but the call is made for older values of the variable. You should update the dependencies of your useCallback:
console.log(plants) inside getCoords might help you investigate.
Try this:
const handler = useCallback(debounce(getCoords, 5000), [plants]);
So it turns out the issue was with my debouncing function. I don't know what exactly the issue was, but everything worked as expected when I replaced the debouncing function with this:
useEffect(() => {
console.log("it changing")
const delayDebounceFn = setTimeout(() => {
getCoords(locationIDObject.id, locationIDObject.index)
}, 4000)
return () => clearTimeout(delayDebounceFn)
},[...plants.map(item => item.location), locationIDObject.id, locationIDObject.index])

Strange bugs with localStorage with useState react

I tried to save values of the input with localStorage and i have a strange bugs, it doens't load data from localStorage.
1- I set the data of multiple input with setItem in useEffect in the InputFile.js
useEffect(() => {
let validateInputs = {
fullNameValidate: enteredFullNameIsValid,
phoneValidate: enteredPhoneIsValid,
};
setValidation(validateInputs);
const allData = { fullNameEntered, phoneEntered };
localStorage.setItem("data", JSON.stringify(allData));
}, [
enteredFullNameIsValid,
enteredPhoneIsValid,
setValidation,
fullNameEntered,
phoneEntered,
]);
2 - i update the value of the handler in the custom hooks (use-input.js) :
const [enteredValue, setEnteredValue] = useState({
enterValidate: "",
});
const valueChangeHandler = (event) => {
setEnteredValue({
...enteredValue,
enterValidate: event.target.value,
});
};
3- I tried to take the values saved with (in custom hooks, use-input.js):
useEffect(() => {
const saved = localStorage.getItem("data");
const { fullNameEntered, phoneEntered } = JSON.parse(saved);
setEnteredValue((prevState) => {
return { ...prevState, fullNameEntered, phoneEntered };
});
}, []);
But it doesn't works!
UPDATE:
here there are the 2 complete files:
1- custom hooks for the inputs
const useInput = (validateValue) => {
const [enteredValue, setEnteredValue] = useState({
enterValidate: "",
});
const [isTouched, setIsTouched] = useState(false);
const [clickClasses, setClickClasses] = useState(false);
const valueIsValid = validateValue(enteredValue.enterValidate);
const hasError = !valueIsValid && isTouched;
const valueChangeHandler = (event) => {
setEnteredValue({
...enteredValue,
enterValidate: event.target.value,
});
};
const inputBlurHandler = () => {
setIsTouched(true);
setClickClasses(false);
};
const inputClickHandler = () => {
setClickClasses(!clickClasses);
};
const reset = () => {
setEnteredValue("");
setIsTouched(false);
};
////TAKE STORED DATA////// BUT IT DOESN'T WORK
useEffect(() => {
const saved = localStorage.getItem("data");
const { fullNameEntered, phoneEntered } = JSON.parse(saved);
setEnteredValue((prevState) => {
return { ...prevState, fullNameEntered, phoneEntered };
});
}, []);
console.log(enteredValue);
return {
value: enteredValue.enterValidate,
isValid: valueIsValid,
hasError,
valueChangeHandler,
inputBlurHandler,
inputClickHandler,
click: clickClasses,
reset,
};
};
export default useInput;
2- Input files
import React, { useEffect } from "react";
import useInput from "../../../../../hooks/use-input";
import validateInput from "../../../../../utils/validateInput";
import {
WrapperNamePhone,
LabelNamePhone,
SpanInputDescription,
InputStyle,
} from "../ContactFormInput.style";
export default function FullNamePhoneInput({ setValidation }) {
//FullName Input
const {
value: fullNameEntered,
isValid: enteredFullNameIsValid,
hasError: fullNameHasError,
valueChangeHandler: fullNameChangeHandler,
inputBlurHandler: fullNameBlurHandler,
inputClickHandler: fullNameClickHandler,
click: fullNameClickClasses,
} = useInput((value) => {
const inputValidFullName = {
value: value,
maxLength: 20,
whiteSpace: true,
allowNumber: false,
allowStrings: true,
};
return validateInput(inputValidFullName);
});
//Phone numbers input
const {
value: phoneEntered,
isValid: enteredPhoneIsValid,
hasError: phoneHasError,
valueChangeHandler: phoneChangeHandler,
inputBlurHandler: phoneBlurHandler,
} = useInput((value) => {
const inputValidPhone = {
value: value,
maxLength: 15,
whiteSpace: true,
allowNumber: true,
allowStrings: false,
};
return validateInput(inputValidPhone);
});
useEffect(() => {
let validateInputs = {
fullNameValidate: enteredFullNameIsValid,
phoneValidate: enteredPhoneIsValid,
};
setValidation(validateInputs);
//STORE DATA////
const allData = { fullNameEntered, phoneEntered };
localStorage.setItem("data", JSON.stringify(allData));
}, [
enteredFullNameIsValid,
enteredPhoneIsValid,
setValidation,
fullNameEntered,
phoneEntered,
]);
//FUll NAME
const borderColorFullName = fullNameHasError ? `rgb(245, 2, 2)` : `#d5d9dc`;
const clickedColor = fullNameClickClasses ? "#2696e8" : "#a4aeb4";
//PHONE NUMBERS
const borderColorPhone = phoneHasError ? `rgb(245, 2, 2)` : `#d5d9dc`;
return (
<WrapperNamePhone>
<LabelNamePhone htmlFor="full-name">
<SpanInputDescription clickedColor={clickedColor}>
Full name
</SpanInputDescription>
<InputStyle
type="text"
name="full-name"
id="full-name"
borderColor={borderColorFullName}
value={fullNameEntered}
onChange={fullNameChangeHandler}
onBlur={fullNameBlurHandler}
onClick={fullNameClickHandler}
/>
{fullNameHasError && <p> - Enter a valid Full Name</p>}
</LabelNamePhone>
<LabelNamePhone htmlFor="phoneNumber">
<InputStyle
placeholder="Enter a valid phone number"
type="text"
name="phoneNumber"
borderColor={borderColorPhone}
value={phoneEntered}
onChange={phoneChangeHandler}
onBlur={phoneBlurHandler}
/>
{phoneHasError && <p> - Enter a valid Phone Number</p>}
</LabelNamePhone>
</WrapperNamePhone>
);
}
///////////////////////////
//////////////////////////
Final Update, i don't like this solution but it works!
I've done like this:
-1 I deleted the setKeys from the Input files.
-2 I update the setKeys dinamically in the use-input hooks:
-3 then with useState i update the getItem!
const [enteredValue, setEnteredValue] = useState(() => {
return {
validateInput: "",
fullName: JSON.parse(localStorage.getItem("fullName")) || "",
phoneNumber: JSON.parse(localStorage.getItem("phoneNumber")) || "",
email: JSON.parse(localStorage.getItem("email")) || "",
country: JSON.parse(localStorage.getItem("country")) || "",
From: JSON.parse(localStorage.getItem("From")) || "",
To: JSON.parse(localStorage.getItem("To")) || "",
};
});
const valueChangeHandler = (event) => {
setEnteredValue({
[event.target.name]: event.target.value,
validateInput: event.target.value,
});
localStorage.setItem(event.target.name, JSON.stringify(event.target.value));
};
*/ Other code for validation, useless in this example */
return {
value: enteredValue.validateInput,
valueName: enteredValue.fullName,
valuePhone: enteredValue.phoneNumber,
valueEmail: enteredValue.email,
valueCountry: enteredValue.country,
valueFrom: enteredValue.From,
valueTo: enteredValue.To,
isValid: valueIsValid,
hasError,
valueChangeHandler,
inputBlurHandler,
inputClickHandler,
click: clickClasses,
reset,
};
I m not sure i have completely understood your problem, however instead of doing all that in useState you could use an effect and set its value based on any dependency or just on component mount like so:
const [enteredValue, setEnteredValue] = useState({});
useEffect(()=>{
const saved = localStorage.getItem("data");
const { fullNameEntered, phoneEntered } = JSON.parse(saved);
setEnteredValue({
...enteredValue,
fullname:fullNameEntered,
phone:phoneEntered
})
},[])
you can change to add some conditions to ensure its not null before being set etc. but this approach should work in general instead of using a callback in useState.
You can then use the values in the component via the state i.e enteredValue?.fullname etc. (?. is optional chaining to prevent undefined errors)

useState referring to stale value

I have a keeper app where I am adding notes and storing them in database. When I make a post request to the server, I am trying to fetch the _id from database, which will eventually help me to later delete the note ( if needed).
Here is my jsx file
function CreateMessage(props) {
const [currentGuest, setCurrentGuest] = useState({
guestName: '',
guestMessage: '',
id:''
});
function handleMessages(event) {
const {name, value} = event.target;
setCurrentGuest(prevGuest => {
return {
...prevGuest,
[name] : value
};
});
}
function submitMessage(event) {
//props.onAdd(currentGuest);
const params = {
guestName: currentGuest.guestName,
guestMessage: currentGuest.guestMessage,
}
axios
.post("http://localhost:8000/notes", params)
.then(res => {
console.log("The response is"+res.data._id);
console.log(res.status);
setCurrentGuest(prevGuest => {
console.log(res.data._id)
return {
...prevGuest,
id: res.data._id
};
});
console.log(currentGuest);
})
event.preventDefault();
}
return (
<div>
<form>
<input
name="guestName"
placeholder="Guest Name"
value={currentGuest.guestName}
onChange={handleMessages}
/>
<textarea
name="guestMessage"
placeholder="Write a Message"
rows="3"
value={currentGuest.guestMessage}
onChange={handleMessages}
/>
<button onClick={submitMessage}>Add</button>
</form>
</div>
);
}
The id is properly being fetched and displayed in ```console.log("The response is"+res.data._id"). But on first submit, the is always empty and stale id gets attached to the currentGuest object on next submit
function submitMessage(event) {
//props.onAdd(currentGuest);
const params = {
guestName: currentGuest.guestName,
guestMessage: currentGuest.guestMessage,
}
axios
.post("http://localhost:8000/notes", params)
.then(res => {
console.log("The response is"+res.data._id);
console.log(res.status);
setCurrentGuest(prevGuest => {
console.log(res.data._id)
return {
...prevGuest,
id: res.data._id
};
});
console.log(currentGuest);
})
event.preventDefault();
}
In the above snippet, after getting the response you're correctly changing the state but the problem is with where you're checking the changed state(console.log(currentGuest)). You're basically logging before the state is changed.
You can use useEffect hook and log the state inside it. This runs every time the currentGuest Changes.
useEffect(() => {
console.log(currentGuest)
}, [currentGuest])
Update
You can use the modified currentGuest inside the useEffect hook:
useEffect(() => {
console.log(currentGuest)
if(currentGuest.id) {
props.onAdd(currentGuest);
// You can also reset the state here as follows
setCurrentGuest({
guestName: '',
guestMessage: '',
id:''
});
}
}, [currentGuest]) // You might need to add the necessary dependencies to this array.

Resources