【问题标题】:Problem with select Field in Formik when onChange option given给出 onChange 选项时 Formik 中选择字段的问题
【发布时间】:2022-01-21 13:29:59
【问题描述】:

我在 Formik 中的 <Field as="select"> 有问题。 onChange 不能正常工作 - 无论我选择什么选项,当我提交表单时,它会将 player1player2 作为空字符串发送。

例如console.log(values)的输出:

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

当我删除 onChange 时,它运行良好,但我需要 onChange 和 selectedPlayer 状态,因为如果我选择 player1,我不希望他在第二个选项中可见,反之亦然。 为什么当selectedPlayer 被更改时,Field 中的value 属性没有更改?似乎它从selectedPlayer 获取了initialValue,它是空字符串。

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);

【问题讨论】:

    标签: javascript reactjs redux formik


    【解决方案1】:

    我认为您不了解 Formik 和这个 Field 组件是如何工作的。 Formik 使用自己的内部状态来处理所有 Form 状态,因此您应该在这种情况下使用它并避免使用 useState。以下是您可以执行的操作:

    • 删除 onChange 处理程序和值,因为它们不允许 Formik 完成其工作。
    • 在允许的情况下使用 Formik 组件下的函数,它将允许您访问 Form 状态值。

    这是代码。我还重构了 array.map 函数,因为在这些场景中使用数组过滤器更正确(map 应该始终返回数组中相同数量的元素)

    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>
      )
    }
    

    【讨论】:

    • 谢谢,我在文档中找不到任何东西,以前不知道它是这样工作的!
    【解决方案2】:

    如果您为 Formik 设置了 onChange 处理程序,那么您将覆盖默认处理程序,并且需要手动处理 setFieldValue

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

    作为替代方案,您可以将 Formik 设置为最顶层的父组件,这将允许其所有子组件像这样使用其上下文:

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

    这样,您不必像使用 useState 那样进行额外的状态管理,它已经在 Formik 的上下文中为您保留。

    请看这里:https://formik.org/docs/api/useFormikContext

    【讨论】:

    • 但是我应该在哪里实现 setFieldValue?
    • @crazyfrog setFieldValue 来自 Formik 道具。就像你有 props.values 你也有 props.setFieldValue 阅读这里了解 props 对象:formik.org/docs/api/formik
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-28
    相关资源
    最近更新 更多