Reputation: 762
I am creating a Update Post component in React. The component gets the existing post data from Firestore and uses a form to update those fields.
EDIT: Short Version:
Long version:
I want to initially set the state of all the fields in the form to the existing values in the post, and then update this state if or when the input field changes (updates).
The problem is:
export default function EditPost() {
// establish state for the form
const [postValue, setPostValue] = useState('')
const [postValueTwo, setPostValueTwo] = useState('')
// state for the query data below
const [postData, setPostData] = useState([])
async function getPost() {
...
setPostData(doc.data());
}
// empty array = SHOULD run once 'after mount'
useEffect(() => {
// run the query
getPost();
// set the state
setPostValue(postData.someValue)
setPostValueTwo(postData.anotherValue)
console.log(postValue) // this returns undefined
},[])
// send data to Firestore
async function updatePost(e) {
e.preventDefault()
return await useFireStore
.collection('posts')
...
.update({
postValue: postValue,
postValueTwo: postValueTwo
});
}
return (
<>
<div>Post Value = {postValue}</div>
<form onSubmit={editPost}>
<input
type="text"
placeholder={postData.someValue}
onChange={(e) => setPostValue(e.target.value)}
/>
<input
type="text"
placeholder={postData.anotherValue}
onChange={(e) => setPostValueTwo(e.target.value)}
/>
</form>
</>
)
}
If you see the div tag before the form, the postValue variable is empty. I know the useEffect runs after the DOM is rendered, which would explain this being empty, but I don't understand why the console.log in the useeffect is empty too.
EDIT => after typing this out I realised in the useEffect the getPost function is of course asynchronous and therefore probably not returning a value before the state is set, so I moved the state setting code to the getPost function to run after the query has been successful, as per below, but the problem is still the same.
async function getPost() {
...
if (!doc.exists) {
console.log('No such document!');
} else {
setPostData(doc.data());
setPostValue(postData.someValue)
setPostValueTwo(postData.anotherValue)
console.log("url is: ",url)
}
}
useEffect(() => {
getPost();
},[])
Sorry for the long-winded question, it's so hard to explain sometimes. Many thanks for any help on this. Cheers, Matt
Upvotes: 1
Views: 963
Reputation: 84912
postData
is a local const
. It will never change, and that's not what setPostData
is trying to do. Calling setPostData
just tells react to render the component again. On that new render a new local const
will be created with the new value, but there's no way for your code that's running now to access that future value in a different closure.
If you need to use the new value, then you'll need to do so with your own local variable. In your second version where it's all done in the getPost
function, that will look like:
async function getPost() {
...
if (!doc.exists) {
console.log('No such document!');
} else {
const newData = doc.data();
setPostData(newData);
setPostValue(newData.someValue);
setpostValuetwo(newData.anotherValue);
}
}
If you want it to be closer to your first version, then you'll need to return the value and use it in the useEffect:
async function getPost() {
...
if (!doc.exists) {
console.log('No such document!');
} else {
const newData = doc.data();
setPostData(newData);
return newData;
}
}
// ...
useEffect(() => {
(async () => {
const newData = await getPost();
setPostValue(newData.someValue)
setPostValueTwo(newData.anotherValue)
})();
},[])
Upvotes: 1