给出 onChange 选项时 Formik 中 select 字段的问题
Problem with select Field in Formik when onChange option given
我的 <Field as="select">
在 Formik 中有问题。 onChange
效果不佳 - 无论我选择什么选项,当我提交表单时,它都会将 player1
和 player2
作为空字符串发送。
例如来自 console.log(values)
的输出:
{ id: '10', player1: '', player2: '', winner: '1', date: '2021-12-16' }
当我删除 onChange
时效果很好,但我需要 onChange 和 selectedPlayer 状态,因为如果我选择 player1
,我不希望他在第二个选项中可见,并且反之亦然。
为什么在更改 selectedPlayer
时 value
属性 在我的 Field
中没有更改?似乎它从空字符串 selectedPlayer
获取初始值。
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);
如果您为 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 的上下文中为您保留。
我认为您不了解 Formik 和 Field 组件的工作原理。 Formik 使用它自己的内部状态来处理所有 Form 状态,因此您应该使用它并避免在这种情况下使用 useState。您可以执行以下操作:
- 删除 onChange 处理程序和值,因为它们不允许 Formik 完成它的工作。
- 在允许的情况下使用 Formik 组件下的函数,它将允许您访问表单状态值。
这是代码。我还重构了 array.map 函数,因为在这些情况下使用数组过滤器更正确(地图应始终 return 与数组中相同数量的元素)
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>
)
}
我的 <Field as="select">
在 Formik 中有问题。 onChange
效果不佳 - 无论我选择什么选项,当我提交表单时,它都会将 player1
和 player2
作为空字符串发送。
例如来自 console.log(values)
的输出:
{ id: '10', player1: '', player2: '', winner: '1', date: '2021-12-16' }
当我删除 onChange
时效果很好,但我需要 onChange 和 selectedPlayer 状态,因为如果我选择 player1
,我不希望他在第二个选项中可见,并且反之亦然。
为什么在更改 selectedPlayer
时 value
属性 在我的 Field
中没有更改?似乎它从空字符串 selectedPlayer
获取初始值。
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);
如果您为 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 的上下文中为您保留。
我认为您不了解 Formik 和 Field 组件的工作原理。 Formik 使用它自己的内部状态来处理所有 Form 状态,因此您应该使用它并避免在这种情况下使用 useState。您可以执行以下操作:
- 删除 onChange 处理程序和值,因为它们不允许 Formik 完成它的工作。
- 在允许的情况下使用 Formik 组件下的函数,它将允许您访问表单状态值。
这是代码。我还重构了 array.map 函数,因为在这些情况下使用数组过滤器更正确(地图应始终 return 与数组中相同数量的元素)
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>
)
}