admin管理员组

文章数量:1302929

I have a problem with my <Field as="select"> in Formik. The onChange doesn't work well - no matter what option I choose, when I submit my form, it sends player1 and player2 as empty string.

E.g. output from console.log(values):

{ id: '10', player1: '', player2: '', winner: '1', date: '2021-12-16' }

When I remove the onChange it works well, but I need onChange and selectedPlayer states, because if I pick a player1, I don't want him to be visible in second option and vice versa. Why the value property doesn't change in my Field when the selectedPlayer is being changed? It seems like it gets the initialValue from selectedPlayer which is empty string.

import { Field, Form, Formik } from 'formik';
import { connect } from 'react-redux';
import {addMatchAction} from "../../ducks/matches/operations";
import {getAllPlayers} from "../../ducks/players/selectors";
import {useState} from "react";
const MatchesForm = ({ addMatchAction,error,players }, props) => {
    const handleSubmit = (values) => {

        console.log(values)
        addMatchAction(values);
    }

    const [selectedPlayer1,setSelectedPlayer1] = useState('')
    const [selectedPlayer2,setSelectedPlayer2] = useState('')
    return (
        <div>
            <h3>Add match</h3>
            <Formik
                initialValues={{
                    id: '',
                    player1: '',
                    player2: '',
                    winner: '',
                    date: ''
                }}
                onSubmit={(values) => handleSubmit(values)}
                enableReinitialize={true}>
                <Form>
                    <label htmlFor="id">Id: </label>
                    <Field name="id" />
                    <label htmlFor="player1">Player 1: </label>
                    <Field as="select" name="player1" onChange={(e) => setSelectedPlayer1(e.target.value)} value={selectedPlayer1}>
                        <option disabled value="">(Select a player 1)</option>
                        {players && players.map(player => {
                            if (player.id !== selectedPlayer2) {
                                return (
                                    <option value={player.id}>{player.firstName} {player.lastName}</option>
                                )
                            }
                        }) }
                    </Field>
                    <p>
                        <label htmlFor="player2">Player 2: </label>
                        <Field as="select" name="player2" onChange={(e) => setSelectedPlayer2(e.target.value)} value={selectedPlayer2}>
                        <option disabled value="">(Select a player 2)</option>
                        {players && players.map(player => {
                            if (player.id !== selectedPlayer1) {
                                return (
                                    <option value={player.id}>{player.firstName} {player.lastName}</option>
                                )
                        }
                        }) }
                        </Field>
                    </p>
                    <label htmlFor="winner">Winner: </label>
                    <Field as="select" name="winner">
                        <option disabled value="">Pick a winner</option>
                        {players && players.map(player => {
                            if (player.id === selectedPlayer1 || player.id === selectedPlayer2 ) {
                                return (
                                    <option value={player.id}>{player.firstName} {player.lastName}</option>
                                )
                            }
                        })}
                    </Field>
                    <label htmlFor="date">Date: </label>
                    <Field name="date" type="date" />
                    <button type="submit">
                        Zatwierdz
                    </button>
                </Form>
            </Formik>
            <p>{error && (<span>{error.name} {error.response.message}</span>)}</p>
        </div>
    )
}

const mapStateToProps = (state) => {
    return {
        error: state.error.error,
        players: getAllPlayers(state)
    }
}

const mapDispatchToProps = ({
    addMatchAction
});


export default connect(mapStateToProps, mapDispatchToProps)(MatchesForm);

I have a problem with my <Field as="select"> in Formik. The onChange doesn't work well - no matter what option I choose, when I submit my form, it sends player1 and player2 as empty string.

E.g. output from console.log(values):

{ id: '10', player1: '', player2: '', winner: '1', date: '2021-12-16' }

When I remove the onChange it works well, but I need onChange and selectedPlayer states, because if I pick a player1, I don't want him to be visible in second option and vice versa. Why the value property doesn't change in my Field when the selectedPlayer is being changed? It seems like it gets the initialValue from selectedPlayer which is empty string.

import { Field, Form, Formik } from 'formik';
import { connect } from 'react-redux';
import {addMatchAction} from "../../ducks/matches/operations";
import {getAllPlayers} from "../../ducks/players/selectors";
import {useState} from "react";
const MatchesForm = ({ addMatchAction,error,players }, props) => {
    const handleSubmit = (values) => {

        console.log(values)
        addMatchAction(values);
    }

    const [selectedPlayer1,setSelectedPlayer1] = useState('')
    const [selectedPlayer2,setSelectedPlayer2] = useState('')
    return (
        <div>
            <h3>Add match</h3>
            <Formik
                initialValues={{
                    id: '',
                    player1: '',
                    player2: '',
                    winner: '',
                    date: ''
                }}
                onSubmit={(values) => handleSubmit(values)}
                enableReinitialize={true}>
                <Form>
                    <label htmlFor="id">Id: </label>
                    <Field name="id" />
                    <label htmlFor="player1">Player 1: </label>
                    <Field as="select" name="player1" onChange={(e) => setSelectedPlayer1(e.target.value)} value={selectedPlayer1}>
                        <option disabled value="">(Select a player 1)</option>
                        {players && players.map(player => {
                            if (player.id !== selectedPlayer2) {
                                return (
                                    <option value={player.id}>{player.firstName} {player.lastName}</option>
                                )
                            }
                        }) }
                    </Field>
                    <p>
                        <label htmlFor="player2">Player 2: </label>
                        <Field as="select" name="player2" onChange={(e) => setSelectedPlayer2(e.target.value)} value={selectedPlayer2}>
                        <option disabled value="">(Select a player 2)</option>
                        {players && players.map(player => {
                            if (player.id !== selectedPlayer1) {
                                return (
                                    <option value={player.id}>{player.firstName} {player.lastName}</option>
                                )
                        }
                        }) }
                        </Field>
                    </p>
                    <label htmlFor="winner">Winner: </label>
                    <Field as="select" name="winner">
                        <option disabled value="">Pick a winner</option>
                        {players && players.map(player => {
                            if (player.id === selectedPlayer1 || player.id === selectedPlayer2 ) {
                                return (
                                    <option value={player.id}>{player.firstName} {player.lastName}</option>
                                )
                            }
                        })}
                    </Field>
                    <label htmlFor="date">Date: </label>
                    <Field name="date" type="date" />
                    <button type="submit">
                        Zatwierdz
                    </button>
                </Form>
            </Formik>
            <p>{error && (<span>{error.name} {error.response.message}</span>)}</p>
        </div>
    )
}

const mapStateToProps = (state) => {
    return {
        error: state.error.error,
        players: getAllPlayers(state)
    }
}

const mapDispatchToProps = ({
    addMatchAction
});


export default connect(mapStateToProps, mapDispatchToProps)(MatchesForm);
Share asked Dec 19, 2021 at 16:21 crazyfrogcrazyfrog 2471 gold badge5 silver badges13 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 3

I think that you are not understanding how Formik and this Field ponent work. Formik is using its own internal state to handle all Form states, so you should be using it and avoiding useState in this scenario. Here is what you can do:

  • Remove onChange handler and value as they are not allowing Formik to do its job.
  • Use the function just under Formik ponent as it is allowed and it will allow you to access the Form state values.

Here's the code. I've also refactored the array.map functions because it is more correct to use an array filter for these scenarios (map should always return the same number of elements in the array)

const MatchesForm = ({ addMatchAction,error,players }, props) => {
  const handleSubmit = (values) => {
      console.log('submit', values)
      addMatchAction(values);
  }

  return (
      <div>
          <h3>Add match</h3>
          <Formik
              initialValues={{
                  id: '',
                  player1: '',
                  player2: '',
                  winner: '',
                  date: ''
              }}
              onSubmit={(values) => handleSubmit(values)}
              enableReinitialize={true}>
                  {props => {
                      // Try a console.log here to see props and props.values and you will see them updating on every change
                      // console.log('Values', props.values);

                      return (
                        <Form>
                        <label htmlFor="id">Id: </label>
                        <Field name="id" />
                        <label htmlFor="player1">Player 1: </label>
                        <Field as="select" name="player1">
                            <option disabled value="">(Select a player 1)</option>
                            {players && players.filter(player => player.id !== props.values.player2).map(player => (
                                <option value={player.id}>{player.firstName} {player.lastName}</option>
                            ))}
                        </Field>
                        <p>
                            <label htmlFor="player2">Player 2: </label>
                            <Field as="select" name="player2" >
                            <option disabled value="">(Select a player 2)</option>
                            {players && players.filter(player => player.id !== props.values.player1).map(player => (
                                <option value={player.id}>{player.firstName} {player.lastName}</option>
                            ))}
                            </Field>
                        </p>
                        <label htmlFor="winner">Winner: </label>
                        <Field as="select" name="winner">
                            <option disabled value="">Pick a winner</option>
                            {players && players.filter(player => player.id === props.values.player1 || player.id === props.values.player2).map(player => (
                                <option value={player.id}>{player.firstName} {player.lastName}</option>
                            ))}
                        </Field>
                        <label htmlFor="date">Date: </label>
                        <Field name="date" type="date" />
                        <button type="submit">
                            Zatwierdz
                        </button>
                    </Form>
          
                      )
                  }}
          </Formik>
          <p>{error && (<span>{error.name} {error.response.message}</span>)}</p>
      </div>
  )
}

If you set the onChange hander for Formik, then you're overwriting the default handler, and would need to manual setFieldValue

i.e.

onChange={(e) => {
  setSelectedPlayer1(e.target.value)
  setFieldValue('player1', e.target.value)
}}

What you could do as an alternative is make Formik your top-most parent ponent, which would allow all its children to consume its context like so:

const ChildComponent = () => {
  const { values } = useFormikContext()
  const player1Value = values['player1']
  return <div>Player 1 is: {player1Value}</div>
}

This way, you don't have to do additional state management like you're doing with useState, it's already kept for you in Formik's context.

See here: https://formik/docs/api/useFormikContext

本文标签: javascriptProblem with select Field in Formik when onChange option givenStack Overflow