useEffect to set value of TextField not working - javascript

I'm attempting to use useEffect to set the initial value of a MUI TextField on load. The value is being pulled from a database. This works the majority of the time however in some cases the useEffect does not correctly update the value of the textfield and it is just blank.
My Question: How can I change my code to make sure that the data being pulled from useEffect always sets the initial value of a textfield on load?
const [introText, setIntroText] = useState();
useEffect(() => {
const fetchResults = async () => {
const result = await axios({
method: "GET",
url: "https://server.site.com/userData",
withCredentials: true,
});
setIntroText(result.data.introEssayText);
};
fetchResults();
}, []);
<TextField
onChange={(e) => setIntroText(e.target.value)}
value={introText}
onInput={handleUpdatedIntro}
/>;

"This works the majority of the time however in some cases the useEffect does not correctly update the value of the textfield and it is just blank". Make sure result.data.introEssayText is not empty for all use cases.
Other than that, having const [introText, setIntroText] = useState() would trigger the below warning for a normal input which I belive TextField would fall back to.
Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
Insted use const [introText, setIntroText] = useState("") so the initial value of the input is "" instead of undifined.

Related

React useState doesn't update even with useEffect added

Probably it is a classic issue with useState which is not updating.
So there is a tree with some checkboxes, some of them are already checked as they map some data from an endpoint.
The user has the possibility to check/uncheck them. There is a "cancel" button that should reset them to the original form.
Here is the code:
const [originalValues, setOriginalValues] = useState<string[]>([]);
...
const handleCancel = () => {
const originalValues = myData || []; //myData is the original data stored in a const
setOriginalValues(() => [...myData]);
};
...
useEffect(() => {
setOriginalValues(originalValues);
}, [originalValues]);
However, it is not working, the tree is not updating as it should. Is it something wrong here?
Just do the following, no need for ()=> the state will update inside the hook if called, plus change the constant it will cause confusion inside your code and protentional name clash later on, with the current state variable name, and also make sure your data are there and you are not injection empty array !!!! which could be the case as well !.
// Make sure data are available
console.log(myData)
// Then change the state
setOriginalValues([...myData]);

How to update default value of MUI TextField?

I'm finishing up the final steps of my form however I have encounter an issue when trying to display the current name of the user in the defaultValue variable of the TextField from MUI.
If a value starts on ""/null or basically empty the defaultValue will stay empty doesn't matter how many times you update that value from ""/null to something, is there a way to fix that?
I have this:
{console.log(tempName)}
<TextField
required
margin="dense"
label='Nombre'
defaultValue= {tempName}
onChange={handleChangeName} />
tempName starts empty but then I update it in a useEffect I have that brings the data of the document of the user I'm log in atm, even after having a value the defaultValue stays empty/null/"".
There's something else I did notice though, if you use the user.displayName directly it does works but only until you refresh (because when you refresh user goes back to null and then like .2 segs is back to his value)
this is how it looks when I use user before refresh
since user brings both name and lastname (displayName, basically) I wanted to split it and all the user information is in a document :
const [tempName, setTempName] = useState("");
const [tempLastName, setTempLastName] = useState("");
const [tempEmail, setTempEmail] = useState("");
const [tempPhone, setTempPhone] = useState("");
const [tempIdType, setTempIdType] = useState("");
const [tempIdTypes, setTempIdTypes] = useState("");
useEffect(() => {
const userDoc = db.collection('usuarios').doc(user.uid);
userDoc.get().then(doc => {
if (doc.exists) {
const tempData = [];
const data = doc.data();
tempData.push(data);
setTempName(tempData[0].name)
setTempLastName(tempData[0].lastName)
setTempEmail(tempData[0].email)
setTempPhone(tempData[0].phone)
setTempIdType(tempData[0].id)
setTempIdTypes(tempData[0].idType)
setOldPassword(tempData[0].password)
}
});
}, [user])
This is how it looks in the firebase
Is that how is suppose to work?
I would have think that defaultValue will update to the value you are giving it... Thank you, I didn't find anything related with this on TextField documentation. All I know is that they add initialized values from the very beginning and not values that get "updated" with time, I just wanted to display the current information of the user in the form.
Have a look at controlled mode example, defaultValue doesn't work on subsequent renders, if you want to control and update the value, use value prop.
Default value is usually used in uncontrolled mode where the value is controlled by the DOM element, not react itself, and the only way to alter it is setup an initial value when it is first mounted. The value of the uncontrolled component can only be accessed (without using ref) when submitting the form or validating the value, if you want to update the state on the fly based on the value as the user types, use controlled mode.
value={value}
onChange={e => setState(e.target.value)}
what's the purpose of defaultValue then? if you can just use placeholders for stuff like "Name" and then use value instead of defaultValue
If you use a placeholder, the default value of that TextField won't be submitted in the form (probably not what you expect here).

Asynchronous way to get the latest change in State with React Hooks

I have started learning React and developing an application using ReactJS. Recently i have moved to React Hooks. I know that in Class Component we can get the latest data from the state with the 2nd argument which is present in setState() like
state = {name: ''};
this.setState({name: name}, () => {console.log(this.state)});
I wanted to know if there is any arguments in React Hooks with which we can get the latest data from it.
I am using React Hooks in the below mentioned way, but upon console logging it always return the previous state
Hooks Eg:
const [socialData, setSocialData] = useState([
{ id: new Date().getTime().toString(), ...newItem }
]);
const onChangeCallback = (index, type, data) => {
let newSocialData = [...socialData];
newSocialData[index] = { ...newSocialData[index], [type]: data };
setSocialData(newSocialData);
onInputChange(newSocialData, formKey);
console.log(newSocialData);
};
The this.setState() second argument is not exactly to get the latest data from the state, but to run some code after the state has been effectively changed.
Remember that setting the state is an asynchronous operation. It is because React needs to wait for other potential state change requests so it can optimize changes and perform them in a single DOM update.
With this.setState() you can pass a function as the first argument, which will receive the latest known state value and should return the new state value.
this.setState((previousState) => {
const newState = previousState + 1;
return newState;
}, () => {
console.log('State has been updated!')
});
With that being said, there are special and rare cases when you need to know exactly when the state change has taken place. In many years of working with React I only faced this scenario once and I consider it a desperate attempt to make things work.
Usually, during a callback execution, like your onChangeCallback you want to change the state as the last thing your function does. If you already know the new state value, why do you want to wait for the real state to change to use it?
The new state value should be a problem to be handled during the next render.
If you want to run some code only when that particular state value changes you can do something like this:
import React, {useState, useEffect, useCallback} from 'react';
function MyComponent() {
const [value, setValue] = useState(false);
const onChangeHandler = useCallback((e) => {
setValue(!!e.target.checked);
}, []);
useEffect(() => {
// THIS WILL RUN ONLY WHEN value CHANGES. VERY SIMILAR TO WHAT YOU ARE TRYING TO DO WITH THE this.setState SECOND ARGUMENT.
}, [value]);
return (
<input type='checkbox' onChange={onChangeHandler} />
);
}
There is also a way to create a custom useState hook, to allow you passing a second argument to setValue and mimic the behavior of this.setState, but internally it would do exactly what I did in the above component. Please let me know if you have any doubt.

How to show/hide some alerts on a form with react hooks useState

I have a form that I want to do a basic validation check. I want to check if any of the values are empy except for the checkboxes, but we can start with just one validation. I thought I could use useState hook, but it always sets the default to true on first submit (when the value is filled out), even tho I am setting the value to true when the input value is filled.
const [nameVal, setNameVal] = useState(false);
const submitForm = async (e) => {
e.preventDefault();
//console.log(e.target.name.value);
if(e.target.name.value === ""){
setNameVal(false);
console.log(nameVal);
}
else{
setNameVal(true);
console.log(nameVal);
}
How do I get the console.log(nameVal) to show true the first time the form is submitted and the target.name.value is filled out. Currently the first time it is false and then every time after that it is true. So everything seems to work as I would like except the initial click. Thanks ahead of time
Let's review how useState works
When you do this
const [nameVal, setNameVal] = useState(false);
you're creating a variable (with an initial value of false) and a function. That means that in your handler (assuming you've filled a value properly)
const submitForm = async (e) => {
e.preventDefault();
if(e.target.name.value === ""){
setNameVal(false);
console.log(nameVal); // --> here it will log true
you will see logged true, because nameVal is the variable from above (from the useState line). When you run setNameVal(false) basically it continues to execute your code, and once that has finished, it fires the reconciliation algorithm in React, which will cause a new render. This time, (the second time), when this line runs
const [nameVal, setNameVal] = useState(false);
nameVal will be true. That's because the initial value is used only on the first render. and because it knows the value of the state has changed, thanks to you calling the setXXX function to update to a new state
You need to consider your functional components as a function of state and props, and every change for all the changes you make on your state, you will render the new output in the next render cycle. So that means - every render has its own props and state
(that's not mine, though, read this amazing article from Dan Abramov which also talks about useEffect https://overreacted.io/a-complete-guide-to-useeffect/)
========================
As to your question, how to "toggle" some message to show with useState ? a general form could be
const [showMessage, setShowMessage] = useState(false)
const someEvent = () => {
// do stuff
if (someCondition) {
setShowMessage(true)
}
}
// more stuff
return (
<div>
{ showMessage && <span>I am a message!</span>}
<Foo onChange={someEvent} />
</div>
)
Explanation:
Initial render. showMessage is false, so { showMessage && <span>I am a message!</span>} will return false (which makes React render nothing)
When some event trigger our function handler we will set showMessage to true.
On next render (the reconciliation algorithm detects a change on state and fires a new render), we see showMessage as true, so the span is rendered
At the initial state, you have namevalue to be false, like so:
const [nameVal, setNameVal] = useState(false);
I'll suggest you set the new state of nameVal like so:
setNameVal(!nameVal);

How to avoid setState in useEffect hook causing second render

I'm writing a custom hook that takes an id as input and then should do the following tasks:
get data for that id synchronously from a store
subscribe to changes in this store and update data accordingly
I came up with the following implementation which has the downside that I am setting state directly from inside the effect hook, causing a second render.
function useData({id}) {
// set initial data on first render
const [data, setData] = useState(Store.getData(id));
useEffect(() => {
// when id changes, set data to whatever is in store at the moment
// here we call setData directly from inside useEffect, causing a second render
setData(Store.getData(id));
// subscribe to changes to update data whenever it changes in the store
return Store.onChange(id, setData);
}, [id]);
return data;
}
A second approach I tried was to add a dummy state that is only there to cause a re-render. In this approach I am directly returning the data received from Store.getData(). This ensures that I will get the freshest data with every render and the useEffect ensures that every onChange trigger will cause a new render.
function useData({id}) {
// adding some dummy state that we only use to force a render
const [, setDummy] = useState({});
const refresh = useCallback(() => {
setDummy({});
}, []);
useEffect(() => {
// subscribe to changes cause a refresh on every change
return Store.onChange(id, refresh);
}, [id, refresh]);
return Store.getData[id];
}
The second approach works well but it feels weird to add this dummy state. Sure, I could put this into another useRefresh hook but I am not sure if this would really be a good practice.
Is there any better way of implementing this, without calling setData directly from inside useEffect and without relying on some unused dummy state?
So by now you use useState inside your hook just to re-trigger rendering of host component once store is changed. How about taking change handler from the outside?
function useData(id, onChanged) {
useEffect(() => {
return store.onChange(id, onChanged);
}, [id, onChanged]);
return store.getData(id);
}

Categories

Resources