0

I have an EditProfile functional component that initially loads perfect, but loses state whenever the user refreshes their page with the form open. Instead of pre-filling the inputs with the 'user' state/values, it now sets the inputs as blank, and the 'user' object from const [user, setUser] = useState({}) console.logs as an empty object.

Here is the EditProfile component:

import React, {useEffect, useState} from 'react'
import './EditProfile.css'


export default function EditProfile(props) {
    
    const [user, setUser] = useState({})

    let handleChange = (e) => {
        setUser({...user, [e.target.name]: e.target.value})
    }
    
    let handleSubmit = async () => {
        let body = {
            _id: user._id,
            email: user.email,
            username: user.username,
            shortDescription: user.shortDescription,
            fullDescription: user.fullDescription,
            paymentInfo: user.paymentInfo,
            publisherAgreement: user.publisherAgreement
        }
        let options = {
            method: 'PUT',
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(body)
        }
        await fetch('/api/users/editProfile', options)
            .then(res => res.json())
            .then(data => {
                this.props.setUserInState(data)
            })
    }

    useEffect(() => {
        (async() => {
            setUser(props.user)
        })()
    },[])
    
    console.log('here is the user: ' + JSON.stringify(user))

    return (
      <form className='editProfileForm' onSubmit={handleSubmit}>
        <input type='hidden' value={user._id}></input>
        <input type='hidden' value={user.email}></input>
        <div>
          <h4>Edit Profile</h4>
          <div className="form-group address-update">
            <label className="inputUD"><span className="label"><b>Username</b></span></label>
            <input type="text" className="form-control" name="username" onChange={handleChange} value={user.username}></input>
          </div>
          <div className="form-group address-update">
            <label className="inputUD"><span className="label"><b>Email</b></span></label>
            <input type="text" className="form-control" name="email" onChange={handleChange} value={user.email}></input>
          </div>
          {user.publisherAgreement &&
            <div>
              <div className="form-group">
                <label className="inputUD"><span className="label"><b>Short Description</b></span></label>
                <textarea type="text" className="form-control" name="shortDescription" onChange={handleChange} value={user.shortDescription}></textarea>
              </div>
              <div className="form-group">
                <label className="inputUD"><span className="label"><b>Full Description</b></span></label>
                <textarea type="text" className="form-control" name="fullDescription" onChange={handleChange} value={user.fullDescription}></textarea>
              </div>
              <div className="form-group address-update">
                <label className="inputUD"><span className="label"><b>Fake Payment Info</b></span></label>
                <input type="text" className="form-control" name="paymentInfo" onChange={handleChange} value={user.paymentInfo}></input>
              </div>
            </div>
          }
          <button type="submit" className="btn btn-success">Submit</button>
        </div>
      </form>
    )
}

And here is the App.jsx component where it is called on (I cut out imports to save space):

state = {
      user: false,
  }

  setUserInState = (incomingUserData) => {
      console.log('incoming userdata: ' + JSON.stringify(incomingUserData))
      this.setState({user: incomingUserData})
  }

  componentDidMount() {
      let token = localStorage.getItem('token')
      if (token) {
          const payload = JSON.parse(atob(token.split('.')[1]))
          if (payload.exp < Date.now() / 1000) {
              localStorage.removeItem('token')
              token = null
          } else {
              this.setState({ user: payload.user })
          }
      }
  }


  render() {
    return (
      <div className="App">
        <Navbar user={this.state.user} setUserInState={this.setUserInState}/>
        <Routes>
          <Route path='/ideas/create' element={<NewIdea user={this.state.user} setUserInState={this.setUserInState}/>}/>
          <Route path='/users/showProfile' element={<UserProfile user={this.state.user} setUserInState={this.setUserInState}/>}/>
          <Route path='/users/editProfile' element={<EditProfile user={this.state.user} setUserInState={this.setUserInState}/>}/>
          <Route path='/publishers/becomePublisher' element={<BecomePublisher user={this.state.user} setUserInState={this.setUserInState}/>} />
          <Route path='/users/addSubscription'/>
          <Route path='/users/removeSubscription'/>
          <Route path='/publishers/show/:id' element={<PubProfile user={this.state.user}/>}/>
          <Route path='/ideas/show/:id' element={<PubIdeas user={this.state.user}/>}/>
          <Route path='/ideas/ideasFeed/:userId' element={<IdeasFeed user={this.state.user}/>}/>
          <Route path='/discover/:id' element={<Discover user={this.state.user} setUserInState={this.setUserInState}/>}/>
          <Route path='*' element={<Navigate to='/discover/:id' replace />}/>
        </Routes>
      </div>
    )
  }
}

The page is accessed via a "Link to" in the Navbar component.

While the form works fine as long as the user doesn't hit refresh, I need the form to be able to handle a refresh and still work.

EDIT/SOLUTION:

I simply passed props.user into the useEffect dependencies array like so:

useEffect(() => {
    (async() => {
        setUser(props.user)
    })()
},[props.user])

The page now sets props into state after a page refresh, just like it does on the first render.

Also, inside my fetch call I switched this.props.setUserInState(data) to just setUser(data).

curtis
  • 17
  • 4
  • Your entire app reloads on a refresh, not just the form, so if you need to preserve state across page reloads, you'll either have to store that state it locally (e.g. localStorage/sessionStorage) or remotely (e.g. with a networked data backend), and then check for preexisting state when your app/components mount. – Mike 'Pomax' Kamermans Jan 27 '23 at 02:03
  • Thing is my compDidMount in App.jsx is already grabbing the user object from local storage and putting it in state, then passing it to this EditProfile component as props. So I should therefore have access to it via props.user, and useEffect/setUser should be putting it in state the way the code is written currently – curtis Jan 27 '23 at 02:11
  • But your form code does not look like it has anything that updates localStorage on form edits prior to submission. It calls the local `setUser` on field changes, but that change never gets cached back into storage, unless I'm missing something? – Mike 'Pomax' Kamermans Jan 27 '23 at 02:13
  • Correct. However I actually managed to console.log props.user on refresh, and I do have the original version from local storage (which is what I want), its just not being set to state by useEffect/setUser on refresh, only on initial load. I'm not concerned about what the user types in those fields being kept after refresh, I just want the original to props.user to be set to state again like on initial load. – curtis Jan 27 '23 at 02:25
  • There is no point in wrapping the `setUser` call into an async IIFE. – Guillaume Brunerie Jan 27 '23 at 16:46
  • @curtis I'm not sure I understand: "first load" and "on refresh" are _the same thing_, a refresh wipes the current page, then loads a new URL, which happens to be the same as the old URL, so your page will run anew, and whatever it loads is "initial load". You may need to explain what you mean with "refresh" if you don't mean the normal browser reload action. – Mike 'Pomax' Kamermans Jan 27 '23 at 17:46
  • I managed to get it to work by passing props.user into the dependencies array. I'm not totally sure on the logic but this helped https://matthewdaly.co.uk/blog/2019/10/27/input-components-with-the-usestate-and-useeffect-hooks-in-react/ – curtis Jan 27 '23 at 18:43

0 Answers0