0

I have decided to use refs to hook into the inputs, In fact I created a custom hook to consume the fetched data and then make the refs!

 function useCreateRefFromProfileDataFromApi(profileDataFromApi) {
    const [profileData, setProfileData] = useState([]);
    const newArray = profileDataFromApi.map((data) => React.createRef())

    useEffect(() => {
      setProfileData(newArray)
    }, [profileDataFromApi.length])

    return profileData
  }

  const profileData = useCreateRefFromProfileDataFromApi(profileDataFromApi);

Which I use in the map function:

    <ul className="list-none">
      {profileDataFromApi.map((data, index) => {
        const { id, name, value } = data
        return (
          <li key={id} className="text-textColor" onClick={() => handleClick(index)}>{name}{' '}
            <input
              ref={profileData[index]}
              type="text"
              value={value}
              className=" mt-1 mb-2 bg-transparent border-0 px-3 py-3 text-textColor   rounded focus:outline-none focus:ring w-full"
              onChange={(e) => {
                handleChange(index, e.target.value)
              }}
            />
          </li>
        )
      })}
    </ul>

However I can't get the onChange function to update the value.

The onClick work as expected:

  function handleClick(index) {
    profileData[index].current.focus();
  }

But not the onChange:

  function handleChange(index, value) {
    profileData[index].current.value = value
  }

So how do I get the handleChange to update, i.e. DOM and profileDataFromApi????

Also this the whole component:

import React, { useEffect, useState, useRef } from 'react';
import { v4 as uuidv4 } from 'uuid';

import getProfile from '../../clientApi/GetProfile'
import { useUser } from '../../lib/hooks'


export default function ProfilePage({ }) {

  const interestedActivitiesRef = useRef();
  const { user } = useUser();

  const [profileDataFromApi, setProfileDataFromApi] = useState([]);
  const [interestedActivities, setInterestedActivities] = useState([])
  const [profileLoaded, setProfileLoaded] = useState(true);
  const [input, setInput] = useState('')

  function useCreateRefFromProfileDataFromApi(profileDataFromApi) {
    const [profileData, setProfileData] = useState([]);
    const newArray = profileDataFromApi.map((data) => React.createRef())

    useEffect(() => {
      setProfileData(newArray)
    }, [profileDataFromApi.length])

    return profileData
  }

  const profileData = useCreateRefFromProfileDataFromApi(profileDataFromApi);


  useEffect(async () => {
    if (user !== undefined && profileLoaded) {
      setProfileLoaded(false);

      const fetchProfile = async () => {
        const { data } = await getProfile();

        const { email } = data[0]._user
        const obj = data[0];
        const { __v, _user, _id, ...profile } = obj;

        profile.email = email;

        for (const profileProp in profile) {

          if (profileProp === 'interested-activities') {
            setInterestedActivities(oldArray => [...oldArray, { id: uuidv4(), 'name': profileProp, 'value': profile[profileProp] }]);
          } else {
            setProfileDataFromApi(oldArray => [...oldArray, { id: uuidv4(), 'name': profileProp, 'value': profile[profileProp] }]);
          }
        }

      }
      fetchProfile()
    }
    return () => { setProfileLoaded(true) }
  }, [])

  function handleClick(index) {
    profileData[index].current.focus();
  }

  function handleChange(index, value) {
    profileData[index].current.value = input
  }

  function handleInterestedActivitiesRefClick(ind) {
    interestedActivitiesRef.current[ind].focus();
  }

  return (<div className="px-4 gap-3 grid">
    <div className="card-body card glass ">
      <div className="avatar online flex flex-row justify-center">
        <div className="rounded-full w-24 h-24">
          <img src="http://daisyui.com/tailwind-css-component-profile-1@94w.png" />
        </div>
      </div>
      <div className="divider glass h-px " />
      <div className="px-4 mt-5">
        <p className="text-sm text-textColor block mb-5">Click on text e.g. "Interested Activities" to upate values.</p>
      </div>

      <div className="px-4 ">
        <ul className="list-none">
          {profileDataFromApi.map((data, index) => {
            const { id, name, value } = data
            return (
              <li key={id} className="text-textColor" onClick={() => handleClick(index)}>{name}{' '}
                <input
                  ref={profileData[index]}
                  type="text"
                  value={value}
                  className=" mt-1 mb-2 bg-transparent border-0 px-3 py-3 text-textColor   rounded focus:outline-none focus:ring w-full"
                  onChange={(e) => {
                    handleChange(index, e.target.value)
                  }}
                />
              </li>
            )
          })}
        </ul>

      </div>
    </div>
  </div >)

}
Antonio Pavicevac-Ortiz
  • 7,239
  • 17
  • 68
  • 141
  • 1
    my guess is that since you are using `refs` when you make a change then the component is not re-rendered & consequently the value is not updated in your `map` fn – Sangeet Agarwal Jan 08 '22 at 18:35

1 Answers1

1

The problem is that you need to differentiate what is the correct way to handle input change when you use controlled-components or uncontrolled-components in this case you are using the onChange event as if you had a controlled-component, in "uncontrolled-components" the value is accessed from input.current.value but you should not update it in every onChange event

Basically these are the differences:

controlled-components: A state is used to store the value of the input every time it changes, also the ideal thing would be to tell the input that its value is the state value, in this way the value will be uniform and always the same. Ref: https://reactjs.org/docs/forms.html#controlled-components

uncontrolled-components: The value of this input is obtained by binding a reference, so when you want to access the value you just have to do input.current.value Ref: https://reactjs.org/docs/uncontrolled-components.html

Marco Mesen
  • 653
  • 6
  • 12