I'm very new to Next.js 14's app router, Supabase Auth, and Supabase Database. This is my first project using all three. My current goal is to have a user create an account, login using a magic link, fill out a profile form where their email address isn't editable and is pre-populated, and all information is sent to the database. Next, I want them to be able to edit that profile form (other than the email address). I want my edit profile form to be pre-populated with the values that the user submitted with the initial profile form.
Currently, the login works fine along with the user filling out their profile information. All information is being sent to the database as expected. When a user goes to edit their profile, all profile information is pre-populated as expected. The problem is that the input fields aren't editable. I can click on an input field to try to update it, but it immediately updates back to the original state. For example, if I edit the name Jessica, I'll click on that input and hit backspace on the letter "a", and the letter disappears and immediately reappears.
I referred to the Supabase docs and React docs, but I haven't found a solution or example that works, and most of my code has been trial and error at this point. I'm not sure where the actual error(s) is.
This is the code relevant to state changes:
const [values, setValues] = useState(null);
const [findData, setFindData] = useState('');
const [email, setEmail] = useState('');
const [loading, setLoading] = useState(true);
This is the code I've written to get the signed-in user's profile:
const getProfile = async () => {
const {
data: { user },
} = await supabase.auth.getUser();
const loadingTimer = setTimeout(() => {
setLoading(false);
setEmail(user.email);
setValues({ ...values, email: user.email });
}, 2000);
const { data, error } = await supabase
.from('clients')
.select()
.eq('email', user.email)
.single()
setFindData(data);
setValues(data);
};
useEffect(() => {
getProfile();
}, [loading, findData]);
I'm not sure if setFindData
is needed here since it's the same as setValues
.
This is my handleChange
function:
const handleChange = (e) => {
setValues({ ...values, [e.target.name]: e.target.value });
};
All of my input fields look similar to this example for the first_name
field:
<input
value={values.first_name}
id='first_name'
name='first_name'
type='text'
required
autoComplete='given-name'
onChange={handleChange}
className='block w-full rounded-md border-0 py-1.5 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-purpleDefault sm:leading-6'
/>
I'm not sure what I'm doing wrong. If I remove findData
from the dependency array in the useEffect
, the data populates the form and then a few seconds later disappears. I've also tried using defaultValue={values.first_name}
instead of value={values.first_name}
in my input field. That route allows the input field to behave as though it's editable (meaning I can change Jessica to Jess), but when I look in my React Dev Tools, the state isn't actually changing.
ETA: I forgot to post the error message in my console: Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.
I wanted to post my answer in case anyone else runs into this problem in the future. I removed const [findData, setFindData] = useState('')
because it was unnecessary. In the part of the getProfile
function where I was pulling data from the database, I needed to clarify was the data was, so I changed that part to const { data: values, error}
. Doing that allowed me to set the values to values
. My dependency array was also incorrect. I removed findData
from the array and replaced it with supabase
. Here's the relevant updated code:
This is the code relevant to state changes:
const [loading, setLoading] = useState(true);
const [email, setEmail] = useState('');
const [values, setValues] = useState('');
This is the code I've written to get the signed-in user's profile:
const getProfile = async () => {
const {
data: { user },
} = await supabase.auth.getUser();
const loadingTimer = setTimeout(() => {
setLoading(false);
setEmail(user.email);
setValues({ ...values, email: user.email });
}, 2000);
const { data: values, error } = await supabase
.from('clients')
.select()
.eq('email', user.email)
.single();
setValues(values);
setPhone(values.phone);
};
useEffect(() => {
getProfile();
}, [loading, supabase]);