reactjsreact-final-formprimereact

React Final Form Dynamic Dropdown with Prime React


I am trying to initialize a dynamic list of User with a DropDown from the PrimeReact library. I am able to load it, even though I am not sure if it is the right way. I am able to select a User, but unable to pass it down to the form. I am also not sure I am supposed to select the User without a state. I am a bit confused on how those interact between each other. It would be really helpful if I could have a bit of guidance.

import { useState, useEffect, useContext } from 'react'
import { Form, Field } from 'react-final-form'
import { Panel } from 'primereact/panel'
import { Dropdown, DropdownProps } from 'primereact/dropdown'
import { Button } from 'primereact/button'

import { api } from '../services' //Axios instance

export interface IUser {
    id: number
    firstName: string
    lastName: string
  }

const selectedUserItemTemplate = (option: IUser) => {
  return <span>{`${option.lastName} ${option.firstName}`}</span>
}

const selectedUserValueTemplate = (
  option: IUser,
  { placeholder }: DropdownProps
) => {
  return option ? (
    <span>{`${option.lastName} ${option.firstName}`}</span>
  ) : (
    <span>{placeholder}</span>
  )
}

const DropDownAdaptater = ({ input, meta, hintText, data, ...rest }: any) => {
  const [selectedUser, setSelectedUser] = useState(undefined)
  const [users, setUsers] = useState<IUser[]>([])
  useEffect(() => {
    const fetchUsers = async () => {
      const { data } = await api.get<IUser[]>('/users')
      setUsers(data)
    }
    fetchUsers()
  }, [])

  console.log(input.value) //Empty -> the form does not update

  return (
    <Dropdown
      {...input}
      value={selectedUser}
      options={users}
      onChange={(e) => setSelectedUser(e.target.value)}
      itemTemplate={selectedUserItemTemplate}
      valueTemplate={selectedUserValueTemplate}
      optionLabel="optionLabel"
      placeholder={hintText}
      style={{ marginRight: '1rem', width: '240px' }}
    />
  )
}

export const Login: React.FC = () => {
  const onSubmitAuthForm = () => {
    console.log('submit')
  }

  return (
    <Panel header="Login">
      <Form onSubmit={onSubmitAuthForm}>
        {({ handleSubmit, form, submitting, pristine, values }) => {
          console.log('values FORM', values) // -----> Empty
          return (
            <form onSubmit={handleSubmit}>
              <h5>Select a user</h5>
              <Field
                name="userName"
                component={DropDownAdaptater}
                hintText="Select a user"
              />
              <Button label="Login" />
            </form>
          )
        }}
      </Form>
    </Panel>
  )
}

Solution

  • Try updating the value your self.

    See here is an example for the same

    const DropDownAdaptater = ({ input, meta, hintText, data, ...rest }: any) => {
      const [selectedUser, setSelectedUser] = useState(undefined)
      const [users, setUsers] = useState<IUser[]>([])
      useEffect(() => {
        const fetchUsers = async () => {
          const { data } = await api.get<IUser[]>('/users')
          setUsers(data)
        }
        fetchUsers()
      }, [])
    
      useEffect(()=>{
          // update the input value, when selected
          // note: it must be a single value and not an object
          input.value= selectedUser.userName; 
      },[selectedUser])
    
      return (
        <Dropdown
          {...input}
          value={selectedUser}
          options={users}
          onChange={(e) => setSelectedUser(e.target.value)}
          itemTemplate={selectedUserItemTemplate}
          valueTemplate={selectedUserValueTemplate}
          optionLabel="optionLabel"
          placeholder={hintText}
          style={{ marginRight: '1rem', width: '240px' }}
        />
      )
    }