【问题标题】:React onChange() not working for dropdown values of a custom object when editing an object from a database从数据库编辑对象时,反应 onChange() 不适用于自定义对象的下拉值
【发布时间】:2019-06-17 07:41:56
【问题描述】:

编辑数据库中现有的文章对象时,我无法更改类别的下拉值。类别是一个对象,它是我的文章对象上的一个属性(字段)。

我在网上进行了很多研究,但无法解决这个特定问题。我可以成功更改值并提交字符串字段的更改 - 例如文章标题和文章正文。

这是代码。问题似乎出在 handleChange() 和/或 <Input type="select" name="category" ...>

class ArticleEdit extends Component {

  emptyItem = {
    articleTitle: '',
    articleText: '',
    imageUrl: '',
    category: {},
    tags: []
  };

  constructor(props) {
    super(props);
      this.state = {
        item: this.emptyItem,
        categories: []
      };
      this.handleChange = this.handleChange.bind(this);
      this.handleSubmit = this.handleSubmit.bind(this);
  }

  async componentDidMount() {
     let allCategories = [];
      if (this.props.match.params.articleId !== 'new') {
        const article = await (await fetch(`/articles/${this.props.match.params.articleId}`)).json();

        fetch ('/categories')
            .then(response => {
                return response.json();
            }).then(data => {
            allCategories = data._embedded.categoryList.map(category => {
                return category
            });

            this.setState({item: article, categories: allCategories});
        });
      }
  }

  handleChange(event) {
    const target = event.target
    const name = target.name;
    const value = target.value;
    let item = {...this.state.item};
    item[name] = value;
    this.setState({item});

    console.log("The category you selected is: " + item.category.categoryName);
    alert("The category you selected is: " + item.category.categoryName);
  }

  async handleSubmit(event) {
    event.preventDefault();
    const {item} = this.state;

    await fetch((item.articleId) ? '/articles/' + (item.articleId) : '/articles', {
      method: (item.articleId) ? 'PUT' : 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(item),
    });
    this.props.history.push('/articles');
  }

  render() {
    const {item} = this.state;

    const categoriesToDisplay = this.state.categories
    const categoryOptionItems = categoriesToDisplay.map(category =>
    <option key={category.categoryId} value={category.categoryName}>{category.categoryName}</option>
    );

    const title = <h2>{item.articleId ? 'Edit Article' : 'Create Article'}</h2>;

    return (
    <div>
      <Header/>
      <Container>
        {title}
        <Form onSubmit={this.handleSubmit}>
          <FormGroup>
            <Label for="articleTitle">Title</Label>
            <Input type="text" name="articleTitle" id="articleTitle" value={item.articleTitle || ''}
                   onChange={this.handleChange} autoComplete="name"/>
          </FormGroup>
          <FormGroup>
            <Label for="articleText">Text</Label>
            <Input type="textarea" name="articleText" id="articleText" value={item.articleText || ''}
                   onChange={this.handleChange}/>
          </FormGroup>
          <FormGroup>
            <Label for="imageUrl">Image URL</Label>
            <Input type="text" name="imageUrl" id="imageUrl" value={item.imageUrl || ''}
                   onChange={this.handleChange}/>
          </FormGroup>
          <div className="row">
            <FormGroup className="col-md-6 mb-3">
              <Label for="category">Select Category</Label>
                <Input type="select" name="category" id="category" value={item.category.categoryName || ''}
                 onChange={value => this.handleChange({target : {name : 'categoryName', value}})}>
                  <option value="">Select</option>
                  {categoryOptionItems}
                </Input>
            </FormGroup>
            <FormGroup className="col-md-6 mb-3">
              <Label for="taqs">Select Tag(s)</Label>
                <Input type="select" name="taqs" id="taqs" value={item.tags.map(tag => tag.tagName) || ''} onChange={this.handleChange} multiple>
                  <option>Depression</option>
                  <option>Anxiety</option>
                  <option>Phobias</option>
                  <option>Psychotherapy</option>
                  <option>Mindfulness</option>
                  <option>Religion</option>
                  <option>Supernatural</option>
                  <option>Healing</option>
                  <option>Eastern Practices</option>
                  <option>Motivation</option>
                  <option>Relationships</option>
                  <option>Positive Thinking</option>
                  <option>Emotions</option>
                  <option>Self-Help</option>
                  <option>Time Management</option>
                  <option>Learning From Experience</option>
                  <option>Personal Development Methods</option>
                </Input>
            </FormGroup>
          </div>
          <FormGroup className="float-right">
            <Button color="primary" type="submit">Save</Button>{' '}
            <Button color="secondary" tag={Link} to="/articles">Cancel</Button>
          </FormGroup>
        </Form>
      </Container>
      <Footer/>
    </div>
    );
  }
}

当我点击文章列表页面上的“编辑”按钮并转到现有文章形式的页面时,我看到在“选择类别”下拉列表中预先选择了我的文章类别。

当我尝试选择另一个类别时,它会发出警报并记录现有类别的名称,请参阅https://ibb.co/k4sj1jq。之后,我在下拉列表中看到现有类别并且无法更改它。

我如何才能成功地为这篇文章选择(并提交)一个新类别?

提前谢谢你。

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    这展示了如何使用“选择”下拉列表来编辑/创建文章类别对象、文章属性以及单击文章上的标记对象时的多选。

    import React, {Component} from 'react';
    import {Link, withRouter} from 'react-router-dom';
    import {Button, Container, Form, FormGroup, Input, Label} from 'reactstrap';
    import Header from './Header';
    import Footer from './Footer';
    
    class ArticleEdit extends Component {
    
      emptyItem = {
        articleTitle: '',
        articleText: '',
        imageUrl: '',
        category: {},
        tags: []
      };
    
      constructor(props) {
        super(props);
          this.state = {
            item: this.emptyItem,
            categories: [],
            allTags: []
          };
          this.handleChange = this.handleChange.bind(this);
          this.handleSubmit = this.handleSubmit.bind(this);
          this.handleTagChange = this.handleTagChange.bind(this);
      }
    
      async componentDidMount() {
         fetch ('/categories')
           .then(response => response.json())
           .then(data => this.setState({categories: data._embedded.categoryList}));
    
         fetch ('/tags')
           .then(response => response.json())
           .then(data => this.setState({allTags: data._embedded.tagList}));
    
         if (this.props.match.params.articleId !== 'new') {
           const article = await (await fetch(`/articles/view/${this.props.match.params.articleId}`)).json();
           this.setState({item: article});
         }
      }
    
      handleChange(event) {
         const target = event.target
         const name = target.name;
         const value = target.value;
    
       	 if (name === "category") {
        	const categoryObject = this.state.categories.find(category => category.categoryId === Number(value));
            this.setState({
    			item: Object.assign({}, this.state.item, {category: categoryObject})
        	});
    	 } else {
    		this.setState({
    			item: Object.assign({}, this.state.item, {[name]: value})
        	});
    	  }
      }
    
      handleTagChange(event) {
         let selectedTags = this.state.item.tags;
         const allTags = this.state.allTags;
         const value = event.target.value;
         let selectedTagIds = selectedTags.map(tag => tag.tagId);
            if (selectedTagIds.includes(Number(value))) {
                selectedTags = selectedTags.filter(t => t.tagId !== Number(value))
            } else {
                var newTagObject = allTags.find(tag => tag.tagId === Number(value))
                selectedTags.push(newTagObject)
            }
         this.setState({
                item: Object.assign({}, this.state.item, {tags: selectedTags})
         });
      }
    
      async handleSubmit(event) {
          if (this.validateFields()) {
          event.preventDefault();
          const {item} = this.state;
          await fetch((item.articleId) ? `/articles/${item.articleId}` : '/articles', {
              method: (item.articleId) ? 'PUT' : 'POST',
              headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
              },
              body: JSON.stringify(item),
            });
          this.props.history.push('/articles');
          }
      }
    
      validateFields() {
         const {item} = this.state;
    
      	 if (item.articleText === "") {
      	    alert('Please provide text for the article');
      	    return false;
      	 }
      	 if (item.articleTitle === "") {
            alert('Please provide a title');
            return false;
         }
         if (!(item.category && Object.keys(item.category).length > 0)) {
            alert('Please select a category');
            return false;
         }
         return true;
      }
    
      render() {
        const {item} = this.state;
    
        const categoryOptions = this.state.categories.map(category =>
            <option key={category.categoryId} name={category.categoryName} value={category.categoryId}>{category.categoryName}</option>
            );
    
        const tagOptions = this.state.allTags.map(tag =>
            <option key={tag.tagId} name={tag.tagName} value={tag.tagId}>{tag.tagName}</option>
            );
    
        const title = <h2>{item.articleId ? 'Edit Article' : 'Create Article'}</h2>;
    
        return (
        <div>
          <Header/>
          <Container>
            {title}
            <Form onSubmit={this.handleSubmit}>
              <FormGroup>
                <Label for="articleTitle">Title</Label>
                <Input type="text" name="articleTitle" id="articleTitle" value={item.articleTitle || ''}
                       onChange={this.handleChange}/>
              </FormGroup>
              <FormGroup>
                <Label for="articleText">Text</Label>
                <Input type="textarea" name="articleText" id="articleText" value={item.articleText || ''}
                       onChange={this.handleChange}/>
              </FormGroup>
              <FormGroup>
                <Label for="imageUrl">Image URL</Label>
                <Input type="text" name="imageUrl" id="imageUrl" value={item.imageUrl || ''}
                       onChange={this.handleChange}/>
              </FormGroup>
              <div className="row">
                <FormGroup className="col-md-6 mb-3">
                  <Label for="category">Select Category</Label>
                  <Input type="select" name="category" id="category"
                  value={(item.category && Object.keys(item.category).length > 0) ? item.category.categoryId : 0} onChange={this.handleChange}>
                    <option>Select</option>
                    {categoryOptions}
                  </Input>
                </FormGroup>
                <FormGroup className="col-md-6 mb-3">
                  <Label for="tags">Select Tag(s)</Label>
                    <Input type="select" name="tags" id="tags" value={item.tags.map(tag => tag.tagId)} onClick={this.handleTagChange} multiple>
                      {tagOptions}
                    </Input>
                </FormGroup>
              </div>
              <FormGroup className="float-right">
                <Button color="primary" type="submit">Save</Button>{' '}
                <Button color="secondary" tag={Link} to="/articles">Cancel</Button>
              </FormGroup>
            </Form>
          </Container>
          <Footer/>
        </div>
        );
      }
    }
    
    export default withRouter(ArticleEdit);

    【讨论】:

      【解决方案2】:
      onChange={value => this.handleChange({target : {name : 'categoryName', value}})}
      

      我相信类别的 onChange 函数将接收事件,而不是值。您可以简化为

      <Input name="categoryName" onChange={this.handleChange} > { options } </Input>
      

      将 onChange 函数替换为以下内容

      onChange={e => 
        this.handleChange({
          ...e,
          target: {
            ...e.target,
            name: 'categoryName'
          }
        })
      }
      

      我注意到的一个问题是您设置fetch ('/categories') 响应的方式不正确。

      fetch ('/categories')
        .then(r => r.json())
        .then(r => this.setState({ categories: r }));
      

      这确保只有在 fetch 调用解决后才会设置 state 中的类别。

      【讨论】:

      • 嗨,阿杰,感谢您的回复。这两种方式都允许在选择后在选择框中看到新类别。我仍然收到旧类别的警报和控制台日志,单击“保存”后旧类别仍然存在。
      • 你能告诉我这个 Input 组件是从哪里来的吗?它是您创建的组件还是您正在使用任何库??
      • 它来自 Reactstrap。
      • 您能否检查类别输入是否正在更改输入。在渲染中放置一个控制台以显示 this.state.category.categoryName 并查看它是否在输入更改时发生变化。
      • 它没有改变。它记录旧类别并在我单击它后将其返回到选择框中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多