What is the correct way to wire up an input field in Redux

社会主义新天地 提交于 2021-02-07 09:47:39

问题


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:

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.

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?


回答1:


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)

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.

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

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.

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.




回答2:


As React already mentioned in the console, you have to provide an "onChange" listener to make that field editable.

The reason behind that is the value property of the input field which you provided

{this.props.recipe.name}

This value doesn't changes when you type in the input field. And since this doesn't changes, you don't see the new value in your input field. (Which makes you feel that it is non editable.)

Another thing to note here is that the "recipe.name" should be transferred to the state of your component so that you can set a new state inside "onChange" listener.

see the usage of "setState" function.



来源:https://stackoverflow.com/questions/43009402/what-is-the-correct-way-to-wire-up-an-input-field-in-redux

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!