在 Redux 中连接输入字段的正确方法是什么

问题描述:

我在 Redux 应用程序中有以下用于食谱的组件,为了简单起见,它目前只有一个名称.

I have the following component in a Redux app for recipes, which currently only has a name right now, for simplicity sake.

class RecipeEditor extends Component {

    onSubmit = (e) => {
        e.preventDefault()

        this.props.updateRecipe(this.props.recipe, { name: this.refs._name.value })
    }

    render = () => {
        if (!this.props.recipe) {
            return <div />
        }

        return (
            <div>
                <form onSubmit={this.onSubmit}>
                    <label>Name: </label>
                    <input type="text" ref="_name" value={this.props.recipe.name} />
                    <input type="submit" value="save" />
                </form>
            </div>)
    }

    static propTypes = {
        recipe: React.PropTypes.shape({
            name: React.PropTypes.string.isRequired
        })
    }
}

这给了我一个无法编辑的文本框的编辑器.控制台中也有警告:

This gives me an editor with a textbox that can't be edited. There's a warning in the console as well:

警告:表单 propType 失败:您向表单提供了 value 道具没有 onChange 处理程序的字段.这将呈现只读场地.如果字段应该是可变的,请使用 defaultValue.除此以外,设置 onChangereadOnly.检查渲染方法食谱编辑器.

Warning: Failed form propType: You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly. Check the render method of RecipeEditor.

这是有道理的,但我不想要 onChange 事件,我将使用 ref 来获取提交时的值.它显然不是只读字段,所以我尝试将其更改为具有 defaultValue.

That makes sense, but I don't want an onChange event, I'll use ref to get the values on submit. It's not a readonly field obviously, so I try changing it to have a defaultValue.

<input type="text" ref="_name" defaultValue={this.props.recipe.name} />

这更接近于我正在寻找的行为,但现在这仅在安装控件时设置配方,并且在选择新配方时不再更新.

This gets closer to the behavior I'm looking for, but now this only sets the recipe when the control is mounted and it no longer updates when a new recipe is chosen.

解决方案是否在每个设置状态的输入字段上都有一个处理程序,然后在提交时获取所有状态并更新配方?

Is the solution having a handler on every input field that sets state, and then in submit, take all the state and update the recipe?

当您使用带有 value 属性集的输入元素时,它会变成一个受控"组件.有关更详细的说明,请参阅此页面:

When you use an input element with the valueattribute set, it becomes a "controlled" component. See this page for a more detailed explanation:

https://facebook.github.io/react/docs/forms.html#controlled-components)

长话短说,这意味着在每次渲染时,您都将 value 属性设置为 props 中的值,除非您还更新 redux 存储中的值,否则该值将保持不变).

Long story short, that means that on every render you are setting the value attribute to the value from the props, which will stay the same unless you also update the value in your redux store).

当输入是不受控制的"时(值属性未显式设置),其内部状态(值字符串)由浏览器隐式处理.

When the input is "uncontrolled" instead (value attribute not explicitly set), its internal state (the value string) is handled implicitly by the browser.

如果出于某种原因,您更喜欢将状态保留在本地,并且您不想在每次值随 onChange 更改时调度 redux 操作,您仍然可以使用 React 组件状态自己管理状态并在提交时调度操作:

If for some reason you prefer to keep the state locally and you don't want to dispatch a redux action every time the value changes with onChange, you can still manage the state yourself using React component state and dispatch the action on submit:

class RecipeEditor extends Component {

    state = {
        recipeName: ''
    }

    onSubmit = (e) => {
        e.preventDefault()

        this.props.updateRecipe(this.props.recipe, { name: this.state.recipeName })
    }

    handleNameChange = (e) => {
        this.setState({ recipeName: e.target.value })
    }

    render = () => {
        if (!this.props.recipe) {
            return <div />
        }

        return (
            <div>
                <form onSubmit={this.onSubmit}>
                    <label>Name: </label>
                    <input type="text" ref="_name" value={this.state.recipeName} onChange={this.handleNameChange} />
                    <input type="submit" value="save" />
                </form>
            </div>)
    }

    static propTypes = {
        recipe: React.PropTypes.shape({
            name: React.PropTypes.string.isRequired
        })
    }
}

在此示例中,每当输入值更改时,您都将当前值存储在状态中.调用 setState 会触发一个新的渲染,在这种情况下,它会将值设置为更新的值.

In this example, whenever the input value changes you store the current value in the state. Calling setState triggers a new render, and in this case it will set the value to the updated one.

最后,请注意,如果您不需要将输入值设置为特定值,则不必使用 onChange.在这种情况下,您可以删除 value 属性并只使用 refs.这意味着,如果您在代码中的其他地方更改存储在 Redux 状态中的值,您将不会看到更改反映在您的输入中.如您所见,您仍然可以使用 intitalValue 将初始值设置为特定的值,只是它不会与您的 redux 存储同步.但是,例如,如果您想重复使用相同的表单来编辑您商店中现有的食谱,这将无法正常工作.

Finally, note you don't have to use onChange if you never need to set the input value to something specific. In this case, you can remove the value attribute and just use refs. That means though that if somewhere else in your code you change the value stored in Redux state, you won't see the change reflected in your input. As you've seen, you still can set the initial value to something specific using intitalValue, it just won't be in sync with your redux store. However this is not going to work well if for example you want to reuse the same form to edit an existing recipe you have in your store.