【问题标题】:onChange event doesn't work as expected when <select> in React当 React 中的 <select> 时,onChange 事件无法按预期工作
【发布时间】:2016-07-21 08:03:48
【问题描述】:

我有一个表格,在编辑时显示有关票证的信息。票据是从 API 作为对象接收的,在我的例子中是 data(例如 apiUrl: {tickets: '/api/tickets'})。 data 包含工单的所有默认状态,例如:优先级、状态和类型。所有 API 数据都在 TicketForm 组件中进行管理。参见下面的组件代码:

class TicketForm extends Component {

    constructor(props) {
        super(props);
        this.handleSelectChange = this.handleSelectChange.bind(this);
        this.save = this.save.bind(this);
        this.state = {
            apiUrl: {tickets: '/api/tickets'},
            data: {
                id: '',
                ticket: '',
                key: '',
                priority: {
                    id: "",
                    name: ""
                },
                status: {
                    id: "",
                    name: ""
                },
                type: {
                    id: "",
                    name: ""
                }
            },
            priority: [],
            status: [],
            type: []
        };
    }

    componentWillMount() {
        //Getting the JSON from APIs
    }

    save() {
        //Saving/Updating data to server
    }

    handleSelectChange(e) {
        for (var i = 0; i < this.state[e.target.name].length; i++) {
            if (this.state[e.target.name][i].id == e.target.value) {
                this.state.data[e.target.name] = this.state[e.target.name][i];
            }
        }
        this.setState({data: this.state.data});
    }

    render() {
        return (
            <table className="table table-striped">
                <tbody>
                <tr>
                    <th>Priority:</th>
                    <td>
                        <SelectForm
                            onChange={this.handleSelectChange}
                            name="priority"
                            options={this.state.priority}
                            selected={this.state.data.priority.name}/>
                    </td>
                </tr>
                <tr>
                    <th>Status:</th>
                    <td>
                        <SelectForm
                            onChange={this.handleSelectChange}
                            name="status" options={this.state.type}
                            selected={this.state.data.type.name}/>
                    </td>
                </tr>
                <tr>
                    <th>Type:</th>
                    <td>
                        <SelectForm
                            onChange={this.handleSelectChange}
                            name="type"
                            options={this.state.status}
                            selected={this.state.data.status.name}/>
                    </td>
                </tr>
                </tbody>

            </table>
        )

    }
}

我想在单击元素时编辑此信息,然后我渲染一个&lt;select&gt;,其中包含从 API 作为数组接收的一些选项,以及可应用于字段的不同选项:优先级、状态和类型。这是与上面不同的 API(例如apiUrl: {tickets: '/api/tickets/type'})。选择option 后,我从所选对象中获取数据并将其替换为初始data 对象。请参阅下面的SelectForm 组件:

export default class SelectForm extends Component {

    constructor(props) {
        super(props);
        this.state = {
            edit: false
        };
    }

    editProject() {
        this.setState({edit: true});
    }

    blurProject() {
        this.setState({edit: false});
    }

    handleChange(e) {
        if (this.props.onChange) {
            this.props.onChange(e);
        }
    }

    renderForm() {
        return (
            <select
                className="form-control"
                name={this.props.name}
                onChange={this.handleChange.bind(this)}
                onBlur={this.blurProject.bind(this)}>
                {(Array.isArray(this.props.options) && this.props.options.length > 0) ? this.props.options.map(option => {
                    return (
                        <option key={option.id} value={option.id}>{option.name}</option>
                    )
                }) : (<option>No Options Found</option>)}
            </select>
        )
    }

    render() {
        if (this.state.edit) {
            return this.renderForm();
        } else {
            return (
                <p onClick={this.editProject.bind(this)}>{this.props.selected}</p>
            )
        }
    }
}

我正在尝试处理更改事件。从选择中选择其他选项时,根据所选选项设置状态并在我选择该选项后在字段中显示新状态。

我以为我做到了,但不知何故,它们之间的场表现得很奇怪。优先级似乎可以正常工作,但是每当我从类型中选择一个选项时,它的状态就会改变,甚至无需触摸它,反之亦然。

我不明白是什么问题,请查看handleSelectChange()函数逻辑,告诉我我做错了什么,或者这个问题的一些解决方案。

更新:

在答案中发现,有关Immutability Helpers 的文档将有所帮助。我尝试使用链接中的一个示例,但得到了相同的结果。请看一下代码,如果我做得对,请告诉我:

handleSelectChange(e) {
    for (var i = 0; i < this.state[e.target.name].length; i++) {
        if (this.state[e.target.name][i].id == e.target.value) {
            var newState = update(this.state, {
                data: {
                    [e.target.name]: { $set: this.state[e.target.name][i] }
                }
            });
        }
    }
    this.setState(newState);
}

【问题讨论】:

  • 您传递给 SelectForm 的 onChange 事件是否没有保持其范围?也许尝试绑定它 - onChange={this.handleSelectChange.bind(this)}
  • @hootstheowl 事件工作正常,除了 setState 之后的数据以一种奇怪的方式处理。
  • "每当我从类型中选择一个选项时,它的状态就会改变,甚至无需触摸它,反之亦然。"这是什么意思。以及你想用handleChange() 实现什么
  • 问题不在于handleChange(),无论如何我正在监听子组件。问题出在handleSelectChange()setState 没有按预期工作。我必须独立更改每个字段,但它们以一种奇怪的方式相互交互。如果我更改一个字段,其他字段会自动更改,但这是不对的。

标签: javascript select reactjs onchange react-jsx


【解决方案1】:

您需要使用immutable helpers 或像这样创建新对象:

this.setState(Object.assign({}, this.state, newState))

统一更新: 尝试做这样的事情:

handleSelectChange(e) {
  const data = Object.assign({}, this.state.data)

  data[e.target.name] = e.target.value;
  for (var i = 0; i < data[e.target.name].length; i++) {
      if (data[e.target.name][i].id == e.target.value) {
          data[e.target.name] = data[e.target.name][i];
      }
  }

  this.setState({ data });
}

【讨论】:

  • 我将阅读文档并了解如何实现它,但同时您能否分享更多关于它应该如何工作的细节?我将不胜感激。因为我现在有点困惑。
  • 我试过你的例子,仍然有同样的问题,现在有点糟糕了。也不应该有 newState 吗?
  • 我阅读了您提供的有关 Immutability Helpers 的文档,并尝试在我的案例中实现它。但我仍然有同样的问题。你能看看上面的更新,我做得对吗?
【解决方案2】:

尝试在 for 循环中定义 setState() 并像这样实现 handleSelectChange()。既然你想设置状态数据值,你不能像this.state.data[e.target.name] = this.state[e.target.name][i];那样做,你需要使用setState()

handleSelectChange(e) {
            this.state.data[e.target.name] = e.target.value;
            var self = this;
            for (var i = 0; i < this.state[e.target.name].length; i++) {
                if (this.state[e.target.name][i].id == e.target.value) {
                    self.setState({data: {[e.target.name]: this.state[e.target.name][i]}});

                }
                self.setState({data: self.state.data});
            }

        }

【讨论】:

  • 我以前试过这种方法,但对我不起作用。无论如何感谢您的意见。
  • @VadimBadea 您在使用这种方法时遇到什么问题。
  • 从我的角度来看,这行:self.setState({data[e.target.name] : this.state[e.target.name][i]}); 语法错误。我认为你的意思是这样写:self.setState({data: {[e.target.name]: this.state[e.target.name][i]}}); 这解决了语法,但不是问题。有了这个前,我选择改变状态和类型消失的优先级。我正在尝试单独设置每个字段的状态,而不是相互依赖。我最初的 sn-p 中的语法似乎只适用于第一个元素而不适用于其他元素。希望这可以为您提供有关该问题的更多详细信息。
猜你喜欢
  • 2021-09-15
  • 1970-01-01
  • 1970-01-01
  • 2021-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多