2017-07-22 91 views
1

我知道這是反應中最常見的問題之一:「爲什麼狀態沒有正確更新我的組件?」而且我搜索了很多東西,但是似乎無法解決這個問題。ReactJs不更新嵌套組件

這個問題最常見的答案是我在某處變異狀態,但我無法發現它。

我試圖創建一個標籤,其中還可以動態地添加表單字段按這篇文章,我發現了一個表格:https://goshakkk.name/array-form-inputs/

一切工作正常,除了當我刪除一個動態添加表格,該表格數組的值不會根據狀態進行更新。

我的代碼如下:

import React, { Component } from 'react'; 

import EditorFooter from '../../editor/moduleEditor/Footer'; 
import EditorHeader from '../../editor/moduleEditor/Header'; 


class Tab extends Component { 

    render() { 

     const currentTabNumber = this.props.currentTabNumber; 
     const cssClass = currentTabNumber === this.props.number ? 'active' : ''; 

     return (
      <button className={`tab ${cssClass}`} onClick={(e) => this.props.switchTab(this.props.number)}>{this.props.name}</button> 
     ) 
    }; 
}; 

class CourseForm extends Component { 

    render() { 
     const index = this.props.index; 

     return (
      <div className="inner-form"> 
       <h3>Kurs {index}</h3> 
       <button className="removeForm" onClick={this.props.handleRemoveCourse(index)}>Remove form</button> 

       <h4>Kursens namn</h4> 
       <input type="text" name="name" defaultValue={this.props.course.name} onChange={this.props.handleInputChange(index)} /> 

       <h4>Årtal</h4> 
       <p className="helper-text">Exempel: 2002-2003</p> 
       <input type="text" name="years" defaultValue={this.props.course.years} onChange={this.props.handleInputChange(index)} /> 
      </div> 
     ) 
    }; 
}; 


class Editable extends Component { 

    constructor(props) { 
     super(props); 

     this.state = { 
      showTab: 1, 
     }; 
    } 

    switchTab = (tabNr) => { 
     this.setState({ showTab: tabNr }); 
    } 

    render() { 

     const courses = this.props.courses; 

     const coursesList = courses.map((course, index) => 
      <CourseForm key={index} index={index} course={course} handleInputChange={this.props.handleCourseChange} handleRemoveCourse={this.props.handleRemoveCourse} /> 
     ); 

     return (
      <div id={`${this.props.moduleItem.ModuleId}_Editor`} className="form notransition"> 
       <EditorHeader templateName="Simple test form" /> 
       <section className="form-inner"> 
        <div className="tabs"> 
         <Tab switchTab={this.switchTab} currentTabNumber={this.state.showTab} number={1} name="Information" /> 
         <Tab switchTab={this.switchTab} currentTabNumber={this.state.showTab} number={5} name="Kurser" /> 
        </div> 
        {this.state.showTab === 1 ? 
         <div className="tab-block"> 

          <h4>Bild</h4> 
          <p className="helper-text">Tänk på att cv-bilden ska vara av bra kvalitet. Låt gärna en fotograf komma och fota hela projektgruppen.</p> 
          <input type="file" name="image" defaultValue={this.props.image} onChange={this.props.handleInputChange} /> 

          <h4>Slogan</h4> 
          <p className="helper-text">Om du skulle skriva en kort reklamslogan om dig själv, vad skulle det vara?</p> 
          <textarea name="slogan" defaultValue={this.props.slogan} onChange={this.props.handleInputChange}></textarea> 

         </div> 
         : 
         null 
        } 
        {this.state.showTab === 5 ? 
         <div className="tab-block"> 
          {coursesList} 
          <button className="addForm" onClick={this.props.handleAddCourse}>Add form</button> 
         </div> 
         : 
         null 
        } 
       </section> 
       <EditorFooter {...this.props} submitForm={this.props.saveForm} /> 
      </div> 
     ); 
    } 
}; 

class Readable extends Component { 
    render() { 
     const props = this.props; 

     return (
      <section className="module align-top notransition"> 

      </section> 
     ); 
    } 
}; 

class Template extends Component { 
    render() { 
     const props = this.props; 

     return (
      <section className="module align-top"> 

      </section> 
     ); 
    } 
}; 


class SimpleTestForm extends Component { 

    constructor(props) { 
     super(props); 

     this.state = { 
      slogan: "", 
      image: "", 
      courses: [{ name: '', years: '' }], 
     }; 
    } 

    onAddCourseForm = (event) => { 
     this.setState({ 
      courses: this.state.educations.concat([{ name: '', years: '' }]) 
     }); 
    } 

    handleCourseChange = (index) => (e) => { 
     const newCourses = this.state.courses.map((course, newIndex) => { 
      if (index !== newIndex) return course; 

      const target = e.target; 
      const value = target.type === 'checkbox' ? target.checked : target.value; 
      const name = target.name; 

      return { ...course, [name]: value }; 
     }); 

     this.setState({ courses: newCourses }); 
    } 

    handleRemoveCourse = (index) => (e) => { 
     this.setState({ 
      courses: this.state.courses.filter((courses, newIndex) => index !== newIndex) 
     }); 
     //this.setState(this.state); 
    } 

    handleAddCourse =() => { 
     this.setState({ 
      courses: this.state.courses.concat([{ name: '', years: '' }]) 
     }); 
    } 

    saveForm = (e) => { 
     console.log(this.state); 
    } 

    handleInputChange = (e) => { 

     const target = e.target; 
     const value = target.type === 'checkbox' ? target.checked : target.value; 
     const name = target.name; 

     this.setState({ 
      [name]: value 
     }); 
    } 

    render() { 

     const inEditMode = this.props.mode === 'edit'; 
     const inTemplateMode = this.props.mode === 'template'; 
     const inReadMode = this.props.mode === 'read'; 

     return (
      <div> 
       {inEditMode ? 
        <Editable {...this.props} 
         handleInputChange={this.handleInputChange} 
         onChangeEditor={this.onChangeEditor} 
         saveForm={this.saveForm} 

         handleCourseChange={this.handleCourseChange} 
         handleAddCourse={this.handleAddCourse} 
         handleRemoveCourse={this.handleRemoveCourse} 

         courses={this.state.courses} 
         slogan={this.state.slogan} 
         image={this.state.image} 
        /> 
        : 
        null 
       } 

       {inReadMode ? 
        <Readable {...this.props} 
        /> 
        : 
        null 
       } 

       {inTemplateMode ? 
        <Template {...this.props} 
        /> 
        : 
        null 
       } 
      </div> 
     ); 
    } 
}; 

module.exports = SimpleTestForm; 

我知道這是一個有點長,我已經試過熬下來。

我的麻煩是,如果我在「Kruser」選項卡下添加三個表單,然後填充它們,然後刪除中間的一個,刪除正確的表單(我可以看到如果我登錄狀態),但它的數據來自下面的表格。直到我切換標籤並返回到Kurser選項卡,它才顯示正確的數據。

回答

2

您不應該使用數組索引作爲鍵。這是我可以確定哪些可能會導致您的問題的一件事。

如果我的數據沒有唯一標識符,我經常使用shortid來生成一個。首先yarn add shortidnpm i --save shortid

import { generate } from 'shortid'; 

// Update, it seems to work better if the unique id is generated on the object itself 

... 

handleAddCourse =() => { 
    this.setState({ 
     courses: this.state.courses.concat([{ name: '', years: '', itemKey: generate() }]) 
    }); 
} 

... 

const coursesList = courses.map((course, index) => 
      <CourseForm 
       key={course.itemKey} 
       index={index} 
       course={course} 
       handleInputChange={this.props.handleCourseChange} 
       handleRemoveCourse={this.props.handleRemoveCourse} /> 
     ); 

希望這可以解決你的問題,如果沒有,至少你聽說過這種反模式。 Here您可以通過示例找到一篇關於此主題的媒體文章。

另一個提示,有一個更短,更乾淨的方式來寫三文魚。

作品,但很難讀

{inReadMode ? 
    <Readable {...this.props} 
    /> 
    : 
    null 
} 

有很多清潔

{inReadMode && 
    <Readable {...this.props} /> 
} 

react docs

+1

謝謝!這工作,但有一個例外...我創建數組項時不得不放置'generate()',而不是當我映射數組''課程:this.state.courses.concat([{name:' ',年:',itemKey:generate()}])' – Winter

+0

如果你更新答案,那麼我會接受它。並感謝ternarys上的提示! :) – Winter

+0

更新了答案,很高興它適合你,愉快的編碼。我想你也把它添加到初始狀態,但我想它是自我解釋給其他讀者 – hyde