React:“ this” ไม่ได้ถูกกำหนดไว้ภายในฟังก์ชั่นส่วนประกอบ


152
class PlayerControls extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      loopActive: false,
      shuffleActive: false,
    }
  }

  render() {
    var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"

    return (
      <div className="player-controls">
        <FontAwesome
          className="player-control-icon"
          name='refresh'
          onClick={this.onToggleLoop}
          spin={this.state.loopActive}
        />
        <FontAwesome
          className={shuffleClassName}
          name='random'
          onClick={this.onToggleShuffle}
        />
      </div>
    );
  }

  onToggleLoop(event) {
    // "this is undefined??" <--- here
    this.setState({loopActive: !this.state.loopActive})
    this.props.onToggleLoop()
  }

ฉันต้องการอัปเดตloopActiveสถานะเป็น toggle แต่thisวัตถุไม่ได้กำหนดไว้ในตัวจัดการ ตามเอกสารการสอนฉันthisควรอ้างถึงส่วนประกอบ ฉันพลาดอะไรไปรึเปล่า?

คำตอบ:


211

ES6 React.Component ไม่ผูกเมธอดเข้ากับตัวเองโดยอัตโนมัติ คุณต้องผูกมันด้วยตัวคุณเองในคอนสตรัคเตอร์ แบบนี้:

constructor (props){
  super(props);

  this.state = {
      loopActive: false,
      shuffleActive: false,
    };

  this.onToggleLoop = this.onToggleLoop.bind(this);

}

23
ถ้าคุณเปลี่ยนคุณสมบัติ onClick ของคุณเป็น() => this.onToggleLoopหลังจากย้ายฟังก์ชั่น onToggleLoop ไปยังคลาสตอบกลับของคุณมันจะทำงานได้เช่นกัน
Sam

71
คุณต้องผูกทุกวิธีของทุก ๆ ปฏิกิริยาหรือไม่? นั่นมันบ้าไปแล้วเหรอ?
Alex L

6
@AlexL มีวิธีที่จะทำโดยไม่ผูกมัดวิธีการอย่างชัดเจน หากคุณใช้ Babel เป็นไปได้ที่จะประกาศทุกวิธีในองค์ประกอบ React เป็นฟังก์ชั่นลูกศร มีตัวอย่างอยู่ที่นี่: babeljs.io/blog/2015/06/07/react-on-es6-plus
Ivan

7
แต่ทำไมยังthisไม่ได้กำหนดตั้งแต่แรก ฉันรู้ว่าthisใน Javascript ขึ้นอยู่กับวิธีเรียกใช้ฟังก์ชัน แต่เกิดอะไรขึ้นที่นี่
ไม่ฝักใฝ่ฝ่ายใด

1
แต่ฉันจะทำสิ่งนี้ได้อย่างไรว่าฟังก์ชั่นไม่ได้ถูกกำหนดไว้จนกระทั่งหลังจากตัวสร้าง? ฉันได้รับ "ไม่สามารถอ่านคุณสมบัติ 'ผูก' ของไม่ได้กำหนด" ถ้าฉันพยายามทำสิ่งนี้ในนวกรรมิกแม้เจ้าฟังก์ชั่นของฉันถูกกำหนดในชั้นเรียน :(
rex

87

มีสองวิธี

หนึ่งคือการเพิ่ม this.onToggleLoop = this.onToggleLoop.bind(this);ในตัวสร้าง

onToggleLoop = (event) => {...}ก็คือฟังก์ชั่นลูกศร

onClick={this.onToggleLoop.bind(this)}แล้วมี


1
ทำไม onToogleLoop = () => {} ทำงานอย่างไร ฉันมีปัญหาเดียวกันและฉันผูกมันไว้ในตัวสร้างของฉัน แต่มันไม่ทำงาน ... และตอนนี้ฉันได้เห็นโพสต์ของคุณและแทนที่วิธีการของฉันด้วยไวยากรณ์ฟังก์ชั่นลูกศรและทำงานได้ คุณช่วยอธิบายให้ฉันฟังได้ไหม
Guchelkaben

5
จากdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...... ; ฟังก์ชั่นลูกศรไม่ได้สร้างสิ่งนี้เองค่านี้ของบริบทการดำเนินการที่ล้อมรอบจะถูกใช้
J. Mark Stevens

1
โปรดทราบว่ามีผลผูกพันแบบอินไลน์ในonClickจะกลับมาฟังก์ชั่นใหม่ทุกการแสดงผลและทำให้ดูเหมือนว่าค่าใหม่ได้รับการผ่านสำหรับเสาที่ล้อเล่นกับshouldComponentUpdateในPureComponents
Ninjakannon

25

เขียนฟังก์ชันของคุณด้วยวิธีนี้:

onToggleLoop = (event) => {
    this.setState({loopActive: !this.state.loopActive})
    this.props.onToggleLoop()
}

ฟังก์ชั่นลูกศรไขมัน

การรวมคีย์เวิร์ดนี้จะเหมือนกันทั้งข้างนอกและข้างในฟังค์ชั่น fat arrow สิ่งนี้แตกต่างจากฟังก์ชั่นที่ประกาศด้วยฟังก์ชันซึ่งสามารถผูกสิ่งนี้กับวัตถุอื่นเมื่อมีการเรียกใช้ การรักษาการเชื่อมโยงนี้สะดวกสำหรับการดำเนินการเช่นการแมป: this.items.map (x => this.doSomethingWith (x))


ReferenceError: fields are not currently supportedถ้าผมทำอย่างนั้นฉันจะได้รับ
Pavel Komarov

มันใช้งานได้ถ้าภายในตัวสร้างฉันพูดแบบนี้. = () => {... }, แต่ฉันถือว่ามันเป็นคนโง่และต้องการหลีกเลี่ยงมันถ้าเป็นไปได้
Pavel Komarov

แย่มากที่คุณไม่สามารถใช้ไวยากรณ์คลาสปกติในการโต้ตอบ!
Kokodoko

11

ฉันวิ่งเข้าไปในการผูกที่คล้ายกันในฟังก์ชั่นการเรนเดอร์และจบลงด้วยการผ่านบริบทthisด้วยวิธีต่อไปนี้:

{someList.map(function(listItem) {
  // your code
}, this)}

ฉันเคยใช้:

{someList.map((listItem, index) =>
    <div onClick={this.someFunction.bind(this, listItem)} />
)}

นั่นเป็นจำนวนมากของฟังก์ชั่นที่ไม่จำเป็นที่คุณกำลังสร้างมีแต่ละครั้งและทุกรายการที่มีการแสดง ...
TJ เด

@TJCrowder ใช่แล้วฟังก์ชั่นเหล่านี้จะถูกสร้างขึ้นใหม่ทุกครั้งที่มีการเรนเดอร์ จะดีกว่าการสร้างฟังก์ชั่นเป็นวิธีการเรียนและผูกพวกเขาเมื่อในชั้นเรียน แต่สำหรับผู้เริ่มต้นผูกพันบริบทคู่มืออาจจะเป็นประโยชน์
duhaime

2

คุณควรสังเกตว่าthisขึ้นอยู่กับว่ามีการเรียกใช้ฟังก์ชันอย่างไรเช่น: เมื่อฟังก์ชั่นถูกเรียกว่าเป็นวิธีการของวัตถุมันthisจะถูกตั้งค่าเป็นวัตถุที่เรียกว่าวิธีการ

thisสามารถเข้าถึงได้ในบริบท JSX เป็นวัตถุองค์ประกอบของคุณดังนั้นคุณสามารถเรียกวิธีการที่คุณต้องการแบบอินไลน์เป็นthisวิธีการ

หากคุณเพิ่งผ่านการอ้างอิงไปยังฟังก์ชั่น / วิธีดูเหมือนว่าปฏิกิริยาจะเรียกมันว่าเป็นฟังก์ชั่นอิสระ

onClick={this.onToggleLoop} // Here you just passing reference, React will invoke it as independent function and this will be undefined

onClick={()=>this.onToggleLoop()} // Here you invoking your desired function as method of this, and this in that function will be set to object from that function is called ie: your component object

1
ขวาคุณยังสามารถใช้บรรทัดแรกเช่นonClick={this.onToggleLoop}โดยมีเงื่อนไขว่าในชั้นองค์ประกอบของคุณคุณได้กำหนดเขตข้อมูล (คุณสมบัติ)onToggleLoop = () => /*body using 'this'*/
22415

1

หากคุณใช้ Babel คุณจะผูก 'this' โดยใช้ตัวดำเนินการเชื่อมโยง ES7 https://babeljs.io/docs/en/babel-plugin-transform-function-bind#auto-self-binding

export default class SignupPage extends React.Component {
  constructor(props) {
    super(props);
  }

  handleSubmit(e) {
    e.preventDefault(); 

    const data = { 
      email: this.refs.email.value,
    } 
  }

  render() {

    const {errors} = this.props;

    return (
      <div className="view-container registrations new">
        <main>
          <form id="sign_up_form" onSubmit={::this.handleSubmit}>
            <div className="field">
              <input ref="email" id="user_email" type="email" placeholder="Email"  />
            </div>
            <div className="field">
              <input ref="password" id="user_password" type="new-password" placeholder="Password"  />
            </div>
            <button type="submit">Sign up</button>
          </form>
        </main>
      </div>
    )
  }

}

0

หากคุณเรียกวิธีการที่คุณสร้างในวิธีวงจรชีวิตเช่น componentDidMount ... คุณสามารถใช้this.onToggleLoop = this.onToogleLoop.bind(this)และฟังก์ชั่นลูกศรอ้วนonToggleLoop = (event) => {...}ได้เท่านั้น

วิธีการปกติของการประกาศของฟังก์ชั่นในตัวสร้างจะไม่ทำงานเพราะวิธีการวงจรจะถูกเรียกว่าก่อนหน้านี้



0

ในกรณีของฉันสำหรับองค์ประกอบไร้สัญชาติที่ได้รับการอ้างอิงกับ forwardRef ฉันต้องทำในสิ่งที่มันพูดที่นี่https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd

จากสิ่งนี้ (onClick ไม่สามารถเข้าถึงเทียบเท่ากับ 'this')

const Com = forwardRef((props, ref) => {
  return <input ref={ref} onClick={() => {console.log(ref.current} } />
})

ถึงสิ่งนี้ (มันใช้งานได้)

const useCombinedRefs = (...refs) => {
  const targetRef = React.useRef()

  useEffect(() => {
    refs.forEach(ref => {
      if (!ref) return

      if (typeof ref === 'function') ref(targetRef.current)
      else ref.current = targetRef.current
    })
  }, [refs])

  return targetRef
}

const Com = forwardRef((props, ref) => {
  const innerRef = useRef()
  const combinedRef = useCombinedRefs(ref, innerRef)

  return <input ref={combinedRef } onClick={() => {console.log(combinedRef .current} } />
})
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.