最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Problem with select Field in Formik when onChange option given - Stack Overflow

programmeradmin1浏览0评论

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

发布评论

评论列表(0)

  1. 暂无评论
ok 不同模板 switch ($forum['model']) { /*case '0': include _include(APP_PATH . 'view/htm/read.htm'); break;*/ default: include _include(theme_load('read', $fid)); break; } } break; case '10': // 主题外链 / thread external link http_location(htmlspecialchars_decode(trim($thread['description']))); break; case '11': // 单页 / single page $attachlist = array(); $imagelist = array(); $thread['filelist'] = array(); $threadlist = NULL; $thread['files'] > 0 and list($attachlist, $imagelist, $thread['filelist']) = well_attach_find_by_tid($tid); $data = data_read_cache($tid); empty($data) and message(-1, lang('data_malformation')); $tidlist = $forum['threads'] ? page_find_by_fid($fid, $page, $pagesize) : NULL; if ($tidlist) { $tidarr = arrlist_values($tidlist, 'tid'); $threadlist = well_thread_find($tidarr, $pagesize); // 按之前tidlist排序 $threadlist = array2_sort_key($threadlist, $tidlist, 'tid'); } $allowpost = forum_access_user($fid, $gid, 'allowpost'); $allowupdate = forum_access_mod($fid, $gid, 'allowupdate'); $allowdelete = forum_access_mod($fid, $gid, 'allowdelete'); $access = array('allowpost' => $allowpost, 'allowupdate' => $allowupdate, 'allowdelete' => $allowdelete); $header['title'] = $thread['subject']; $header['mobile_link'] = $thread['url']; $header['keywords'] = $thread['keyword'] ? $thread['keyword'] : $thread['subject']; $header['description'] = $thread['description'] ? $thread['description'] : $thread['brief']; $_SESSION['fid'] = $fid; if ($ajax) { empty($conf['api_on']) and message(0, lang('closed')); $apilist['header'] = $header; $apilist['extra'] = $extra; $apilist['access'] = $access; $apilist['thread'] = well_thread_safe_info($thread); $apilist['thread_data'] = $data; $apilist['forum'] = $forum; $apilist['imagelist'] = $imagelist; $apilist['filelist'] = $thread['filelist']; $apilist['threadlist'] = $threadlist; message(0, $apilist); } else { include _include(theme_load('single_page', $fid)); } break; default: message(-1, lang('data_malformation')); break; } ?>