React - เปลี่ยนอินพุตที่ไม่ได้ควบคุม


359

ฉันมีองค์ประกอบการตอบสนองที่เรียบง่ายด้วยแบบฟอร์มซึ่งฉันเชื่อว่ามีอินพุตที่ควบคุมได้หนึ่งรายการ:

import React from 'react';

export default class MyForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {}
    }

    render() {
        return (
            <form className="add-support-staff-form">
                <input name="name" type="text" value={this.state.name} onChange={this.onFieldChange('name').bind(this)}/>
            </form>
        )
    }

    onFieldChange(fieldName) {
        return function (event) {
            this.setState({[fieldName]: event.target.value});
        }
    }
}

export default MyForm;

เมื่อฉันเรียกใช้แอปพลิเคชันของฉันฉันจะได้รับคำเตือนต่อไปนี้:

คำเตือน: MyForm กำลังเปลี่ยนอินพุตข้อความที่ไม่สามารถควบคุมได้เพื่อควบคุม องค์ประกอบอินพุตไม่ควรสลับจากที่ไม่มีการควบคุมเป็นการควบคุม (หรือในทางกลับกัน) ตัดสินใจระหว่างการใช้อิลิเมนต์อินพุตที่ควบคุมหรือไม่มีการควบคุมสำหรับอายุการใช้งานของส่วนประกอบ

ฉันเชื่อว่าอินพุตของฉันถูกควบคุมเนื่องจากมีค่า ฉันสงสัยว่าฉันทำอะไรผิด

ฉันใช้ React 15.1.0

คำตอบ:


509

ฉันเชื่อว่าอินพุตของฉันถูกควบคุมเนื่องจากมีค่า

เพื่อให้อินพุตถูกควบคุมค่าของมันจะต้องสอดคล้องกับของตัวแปรสถานะ

เงื่อนไขนั้นไม่ตรงกับตัวอย่างของคุณในตอนแรกเนื่องจากthis.state.nameไม่ได้ตั้งค่าไว้ในตอนแรก ดังนั้นอินพุตจะไม่ถูกควบคุมในขั้นต้น เมื่อonChangeตัวจัดการถูกทริกเกอร์เป็นครั้งแรกให้this.state.nameตั้งค่า ณ จุดนั้นเงื่อนไขดังกล่าวข้างต้นเป็นที่พอใจและถือว่าการป้อนข้อมูลได้รับการควบคุม การเปลี่ยนจากการที่ไม่มีการควบคุมเป็นการควบคุมทำให้เกิดข้อผิดพลาดที่เห็นด้านบน

โดยเริ่มต้นthis.state.nameในการสร้าง:

เช่น

this.state = { name: '' };

อินพุตจะถูกควบคุมตั้งแต่เริ่มต้นแก้ไขปัญหา ดูที่React Components ที่ควบคุมสำหรับตัวอย่างเพิ่มเติม

ไม่เกี่ยวข้องกับข้อผิดพลาดนี้คุณควรมีการส่งออกเริ่มต้นหนึ่งรายการเท่านั้น รหัสของคุณด้านบนมีสอง


7
มันยากที่จะอ่านคำตอบและทำตามความคิดหลาย ๆ ครั้ง แต่คำตอบนี้เป็นวิธีที่สมบูรณ์แบบในการเล่าเรื่องและทำให้ผู้ชมเข้าใจในเวลาเดียวกัน ตอบระดับเทพ!
surajnew55

2
ถ้าคุณมีเขตข้อมูลแบบไดนามิกในการวนซ้ำ เช่นคุณตั้งชื่อฟิลด์เป็นname={'question_groups.${questionItem.id}'}?
3574492

2
ฟิลด์ไดนามิกจะทำงานอย่างไร ฉันกำลังสร้างฟอร์มแบบไดนามิกและจากนั้นตั้งชื่อเขตข้อมูลในวัตถุภายในสถานะฉันยังต้องตั้งค่าชื่อเขตข้อมูลด้วยตนเองก่อนแล้วจึงโอนไปยังวัตถุของฉันหรือไม่
โจเซฟ

13
คำตอบนี้ไม่ถูกต้องเล็กน้อย อินพุตถูกควบคุมหากvalueprop มีค่าที่ไม่เป็นนัล / ไม่ได้กำหนด เสาไม่จำเป็นต้องสอดคล้องกับตัวแปรสถานะ (มันอาจจะเป็นค่าคงที่และองค์ประกอบจะยังคงได้รับการพิจารณาควบคุม) คำตอบของอดัมนั้นถูกต้องมากขึ้นและควรได้รับการยอมรับ
ecraig12345

2
ใช่ - นี่เป็นเทคนิคคำตอบที่ผิด (แต่มีประโยชน์) หมายเหตุเกี่ยวกับสิ่งนี้ - ถ้าคุณกำลังจัดการกับปุ่มตัวเลือก (ซึ่ง 'ค่า' ถูกควบคุมแตกต่างกันเล็กน้อย) คำเตือนการตอบสนองนี้สามารถโยนได้จริงแม้ว่าองค์ประกอบของคุณจะถูกควบคุม (ปัจจุบัน) github.com/facebook/react/issues/6779และสามารถแก้ไขได้โดยการเพิ่ม !! เพื่อความจริงของ isChecked
mheavers

123

เมื่อคุณทำให้องค์ประกอบของคุณthis.state.nameไม่ได้ตั้งค่าดังนั้นจึงประเมินundefinedหรือnullและคุณจะจบลงผ่านvalue={undefined}หรือ ที่คุณvalue={null}input

เมื่อตรวจสอบ ReactDOM เพื่อดูว่าข้อมูลที่มีการควบคุมตรวจสอบเพื่อดูว่าvalue != null (หมายเหตุว่ามันเป็น!=ไม่ได้!==) และตั้งแต่undefined == nullใน JavaScript ก็ตัดสินใจว่ามันเป็นไม่สามารถควบคุมได้

ดังนั้นเมื่อonFieldChange()ถูกเรียกใช้this.state.nameจะถูกตั้งค่าเป็นค่าสตริงข้อมูลของคุณจะถูกควบคุมโดยไม่มีการควบคุม

ถ้าคุณทำthis.state = {name: ''}ในคอนสตรัคของคุณเพราะ'' != nullข้อมูลของคุณจะมีค่าตลอดเวลาและข้อความนั้นจะหายไป


5
สำหรับสิ่งที่คุ้มค่าสิ่งเดียวกันสามารถเกิดขึ้นได้this.props.<whatever>ซึ่งเป็นปัญหาของฉัน ขอบคุณอดัม!
อย่า

อ้อ! นอกจากนี้ยังอาจเกิดขึ้นได้หากคุณผ่านตัวแปรคำนวณที่คุณกำหนดในหรือการแสดงออกในแท็กของตัวเองอะไรที่ประเมินrender() undefinedดีใจที่ฉันช่วยได้!
Leigh Brenecki

1
ขอบคุณสำหรับคำตอบนี้แม้ว่าจะไม่ใช่คำตอบที่ยอมรับ แต่มันก็อธิบายปัญหาได้ดีกว่า "ระบุname"
machineghost

1
ฉันได้อัปเดตคำตอบของฉันเพื่ออธิบายวิธีการทำงานของอินพุตควบคุม มันน่าสังเกตว่าสิ่งที่สำคัญที่นี่ไม่ใช่ว่าคุณค่าของundefinedการส่งผ่านในตอนแรก แต่เป็นความจริงที่this.state.nameไม่มีอยู่ในตัวแปรสถานะที่ทำให้อินพุตไม่สามารถควบคุมได้ ตัวอย่างเช่นการมีthis.state = { name: undefined };ผลในการป้อนข้อมูลจะถูกควบคุม ควรเข้าใจว่าสิ่งสำคัญคืออะไรคุณค่ามาจากอะไรไม่ใช่คุณค่า
fvgs

1
@fvgs การที่this.state = { name: undefined }ยังคงส่งผลให้อินพุตที่ไม่สามารถควบคุมได้ <input value={this.state.name} />desugars React.createElement('input', {value: this.state.name})ไป เนื่องจากการเข้าถึงคุณสมบัติที่ไม่มีอยู่ของวัตถุจะส่งคืนค่าundefinedนี้จะประเมินการเรียกใช้ฟังก์ชันเดียวกันที่แน่นอน - React.createElement('input', {value: undefined})ไม่nameได้ตั้งค่าหรือตั้งค่าอย่างชัดเจนundefinedดังนั้นปฏิกิริยาจะทำงานในลักษณะเดียวกัน คุณสามารถเห็นพฤติกรรมนี้ใน JSFiddle นี้
Leigh Brenecki

52

อีกวิธีหนึ่งที่สามารถตั้งค่าเริ่มต้นภายในอินพุตของคุณเช่นนี้:

 <input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>

1
<ใส่ชื่อ = "ชื่อ" type = "ข้อความ" defaultValue = "" onChange = {this.onFieldChange ( 'ชื่อ') ผูก (นี้).} /> ผมคิดว่านี่จะยังทำงาน
แกนดัล์ฟ

ขอบคุณมากนี่คือสิ่งที่ฉันกำลังมองหา มีข้อเสียหรือปัญหาที่อาจเกิดขึ้นจากการใช้รูปแบบนี้ที่ฉันควรทราบหรือไม่?
Marcel Otten

สิ่งนี้ทำงานได้สำหรับฉันเนื่องจากมีการแสดงผลฟิลด์จาก API แบบไดนามิกดังนั้นฉันจึงไม่ทราบชื่อของฟิลด์เมื่อติดตั้งส่วนประกอบ วิธีนี้ใช้ได้ผล!
Jamie - Fenrir Digital Ltd

18

ฉันรู้ว่าคนอื่นตอบคำถามนี้ไปแล้ว แต่ปัจจัยที่สำคัญมากที่นี่อาจช่วยให้ผู้อื่นประสบปัญหาคล้ายกัน:

คุณต้องมีonChangeตัวจัดการเพิ่มในฟิลด์อินพุตของคุณ (เช่นฟิลด์ข้อความช่องทำเครื่องหมายวิทยุ ฯลฯ ) จัดการกิจกรรมผ่านonChangeตัวจัดการเสมอ

ตัวอย่าง:

<input ... onChange={ this.myChangeHandler} ... />

เมื่อคุณกำลังทำงานกับช่องทำเครื่องหมายที่คุณอาจต้องจัดการของรัฐที่มีchecked!!

ตัวอย่าง:

<input type="checkbox" checked={!!this.state.someValue} onChange={.....} >

การอ้างอิง: https://github.com/facebook/react/issues/6779#issuecomment-326314716


1
ทำงานได้ดีสำหรับฉันขอบคุณใช่ฉันเป็น 100% เปอร์เซ็นต์เห็นด้วยว่าสถานะเริ่มต้นคือ {} ดังนั้นค่าที่ตรวจสอบจะไม่ได้กำหนดและทำให้ไม่สามารถควบคุมได้
Ping Woo

13

วิธีแก้ไขปัญหาอย่างง่าย ๆ ในการแก้ไขปัญหานี้คือการกำหนดค่าว่างโดยปริยาย:

<input name='myInput' value={this.state.myInput || ''} onChange={this.handleChange} />

8

ข้อเสียที่เป็นไปได้หนึ่งข้อด้วยการตั้งค่าฟิลด์เป็น "" (สตริงว่าง) ในตัวสร้างคือถ้าฟิลด์เป็นฟิลด์ตัวเลือกและไม่มีการแก้ไขใด ๆ ถ้าคุณไม่ทำการนวดก่อนที่จะโพสต์แบบฟอร์มของคุณเขตข้อมูลจะถูกเก็บไว้ในที่เก็บข้อมูลของคุณเป็นสตริงว่างเปล่าแทนที่จะเป็น NULL

ทางเลือกนี้จะหลีกเลี่ยงสตริงว่าง:

constructor(props) {
    super(props);
    this.state = {
        name: null
    }
}

... 

<input name="name" type="text" value={this.state.name || ''}/>

6

เมื่อคุณใช้onChange={this.onFieldChange('name').bind(this)}ในการป้อนข้อมูลของคุณคุณจะต้องประกาศสตริงว่างสถานะของคุณเป็นค่าของเขตข้อมูลทรัพย์สิน

วิธีที่ไม่ถูกต้อง:

this.state ={
       fields: {},
       errors: {},
       disabled : false
    }

วิธีที่ถูกต้อง:

this.state ={
       fields: {
         name:'',
         email: '',
         message: ''
       },
       errors: {},
       disabled : false
    }

6

ในกรณีของฉันฉันขาดอะไรที่สำคัญไปหน่อย

<input value={state.myObject.inputValue} />

สถานะของฉันเป็นดังต่อไปนี้เมื่อฉันได้รับคำเตือน:

state = {
   myObject: undefined
}

ด้วยการสลับสถานะของฉันเพื่ออ้างอิงอินพุตของค่าของฉันปัญหาของฉันได้รับการแก้ไข:

state = {
   myObject: {
      inputValue: ''
   }
}

2
ขอบคุณที่ช่วยให้ฉันเข้าใจปัญหาที่แท้จริงที่ฉันมี
โรตี - ดีที่สุด

3

หากอุปกรณ์ประกอบฉากของคุณถูกส่งเป็นสถานะให้ใส่ค่าเริ่มต้นสำหรับแท็กอินพุตของคุณ

<input type="text" placeholder={object.property} value={object.property ? object.property : ""}>


3

การอัปเดตสำหรับสิ่งนี้ สำหรับ React Hooks ใช้const [name, setName] = useState(" ")


ขอบคุณ 4 การอัปเดตจอร์แดนข้อผิดพลาดนี้ค่อนข้างยากที่จะแก้ปัญหา
Juan Salvador

ทำไม" "ไม่""? สิ่งนี้ทำให้คุณสูญเสียข้อความคำใบ้ใด ๆ และถ้าคุณคลิกและพิมพ์คุณจะได้รับพื้นที่ส่อเสียดก่อนที่ข้อมูลใด ๆ ที่คุณป้อน
Joshua Wade

1

โดยทั่วไปจะเกิดขึ้นเฉพาะเมื่อคุณไม่ได้ควบคุมค่าของการยื่นเมื่อแอปพลิเคชั่นเริ่มต้นและหลังจากเหตุการณ์บางอย่างหรือบางฟังก์ชั่นเริ่มทำงานหรือมีการเปลี่ยนแปลงสถานะตอนนี้คุณกำลังพยายามควบคุมค่าในฟิลด์อินพุต

การเปลี่ยนแปลงนี้ไม่ได้มีการควบคุมอินพุตและจากนั้นมีการควบคุมมันเป็นสิ่งที่ทำให้เกิดปัญหาในสถานที่แรก

วิธีที่ดีที่สุดในการหลีกเลี่ยงปัญหานี้คือการประกาศค่าบางอย่างสำหรับอินพุตใน Constructor ของส่วนประกอบ เพื่อให้องค์ประกอบอินพุตมีค่าตั้งแต่เริ่มต้นแอปพลิเคชัน


0

สำหรับการตั้งค่าคุณสมบัติของรัฐแบบไดนามิกสำหรับการป้อนข้อมูลในแบบฟอร์มและทำให้ควบคุมได้คุณสามารถทำสิ่งนี้:

const inputs = [
    { name: 'email', type: 'email', placeholder: "Enter your email"},
    { name: 'password', type: 'password', placeholder: "Enter your password"},
    { name: 'passwordConfirm', type: 'password', placeholder: "Confirm your password"},
]

class Form extends Component {
  constructor(props){
    super(props)
    this.state = {} // Notice no explicit state is set in the constructor
  }

  handleChange = (e) => {
    const { name, value } = e.target;

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

  handleSubmit = (e) => {
    // do something
  }

  render() {
     <form onSubmit={(e) => handleSubmit(e)}>
       { inputs.length ?
         inputs.map(input => {
           const { name, placeholder, type } = input;
           const value = this.state[name] || ''; // Does it exist? If so use it, if not use an empty string

           return <input key={name}  type={type} name={name} placeholder={placeholder} value={value} onChange={this.handleChange}/>
       }) :
         null
       }
       <button type="submit" onClick={(e) => e.preventDefault }>Submit</button>
     </form>    
  }
}

0

เพียงสร้างทางเลือกเป็น '' ถ้า this.state.name เป็นโมฆะ

<input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>

นอกจากนี้ยังใช้งานได้กับตัวแปร useState

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.