Phản ứng: Hồi này, không xác định được bên trong một chức năng thành phần


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()
  }

Tôi muốn cập nhật loopActivetrạng thái khi chuyển đổi, nhưng thisđối tượng không được xác định trong trình xử lý. Theo tài liệu hướng dẫn, tôi thisnên tham khảo thành phần. Tui bỏ lỡ điều gì vậy?

Câu trả lời:


211

ES6 React.Component không tự động liên kết các phương thức với chính nó. Bạn cần phải ràng buộc chúng trong constructor. Như thế này:

constructor (props){
  super(props);

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

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

}

23
nếu bạn thay đổi thuộc tính onClick của mình thành () => this.onToggleLoopsau khi chuyển hàm onToggleLoop vào lớp phản ứng thì nó cũng sẽ hoạt động.
Sam

71
Bạn có thực sự phải ràng buộc mọi phương thức của mọi lớp phản ứng không? Điều đó có hơi điên không?
Alex L

6
@AlexL Có nhiều cách để làm điều đó mà không ràng buộc rõ ràng các phương thức. nếu bạn sử dụng babel, có thể khai báo mọi phương thức trên thành phần React là các hàm mũi tên. Có các ví dụ ở đây: babeljs.io/blog/2015/06/07/react-on-es6-plus
Ivan

7
Nhưng tại sao thiskhông xác định ở nơi đầu tiên? Tôi biết thistrong Javascript phụ thuộc vào cách gọi hàm, nhưng điều gì đang xảy ra ở đây?
maverick

1
nhưng làm thế nào tôi có thể làm điều này là chức năng không được xác định cho đến sau khi xây dựng? Tôi nhận được "Không thể đọc thuộc tính 'liên kết' không xác định" nếu tôi cố gắng thực hiện điều này trong hàm tạo ngay cả khi chức năng của tôi được xác định trên lớp :(
rex

86

Có một vài cách khác nhau.

Một là thêm this.onToggleLoop = this.onToggleLoop.bind(this);vào constructor.

Một chức năng khác là mũi tên onToggleLoop = (event) => {...}.

Và sau đó là có onClick={this.onToggleLoop.bind(this)}.


1
Tại sao onToogleLoop = () => {} hoạt động? Tôi gặp vấn đề tương tự và tôi liên kết nó trong hàm tạo của mình nhưng nó không hoạt động ... và bây giờ tôi đã thấy bài đăng của bạn và thay thế phương thức của tôi bằng cú pháp hàm mũi tên và nó hoạt động. Bạn có thể giải thích điều đó cho tôi không ?
Guchelkaben

5
từ developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/iêu ; Hàm mũi tên không tự tạo cái này, giá trị này của bối cảnh thực thi kèm theo được sử dụng.
J. Mark Stevens

1
Lưu ý rằng ràng buộc nội tuyến trong onClicksẽ trả về một chức năng mới mỗi lần kết xuất và do đó, có vẻ như một giá trị mới đã được truyền cho prop, gây rối shouldComponentUpdatetrong PureComponents.
Ninjakannon

24

Viết hàm của bạn theo cách này:

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

Chức năng Mũi tên Chất béo

ràng buộc cho từ khóa này là giống nhau bên ngoài và bên trong chức năng mũi tên chất béo. Điều này khác với các hàm được khai báo với hàm, có thể liên kết nó với một đối tượng khác khi gọi. Việc duy trì liên kết này rất thuận tiện cho các hoạt động như ánh xạ: this.items.map (x => this.doSthingWith (x)).


Nếu tôi làm điều đó tôi nhận được ReferenceError: fields are not currently supported.
Pavel Komarov

Nó hoạt động nếu bên trong hàm tạo tôi nói điều này.func = () => {...}, nhưng tôi coi đó là loại ngớ ngẩn và muốn tránh nó nếu có thể.
Pavel Komarov

Thật khủng khiếp khi bạn không thể sử dụng cú pháp lớp bình thường trong React!
Kokodoko

11

Tôi đã chạy vào một liên kết tương tự trong một chức năng kết xuất và cuối cùng đã vượt qua bối cảnh thistheo cách sau:

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

Tôi cũng đã sử dụng:

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

Đó là rất nhiều chức năng không cần thiết mà bạn đang tạo ở đó, mỗi lần danh sách được hiển thị ...
TJ Crowder

@TJCrowder Có, đúng là các chức năng này được tạo ra mỗi lần kết xuất được gọi. Tốt hơn là tạo các hàm như các phương thức lớp và liên kết chúng một lần với lớp, nhưng đối với người mới bắt đầu, ràng buộc bối cảnh thủ công có thể hữu ích
duhaime

2

Bạn nên chú ý rằng thisphụ thuộc vào cách gọi hàm, tức là: khi một hàm được gọi là phương thức của một đối tượng, nó thisđược đặt thành đối tượng mà phương thức được gọi.

thiscó thể truy cập trong ngữ cảnh JSX làm đối tượng thành phần của bạn, vì vậy bạn có thể gọi nội tuyến của phương thức bạn muốn là thisphương thức.

Nếu bạn chỉ chuyển tham chiếu đến hàm / phương thức, có vẻ như phản ứng sẽ gọi nó là hàm độc lập.

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
Đúng, bạn thậm chí có thể sử dụng dòng đầu tiên, onClick={this.onToggleLoop}miễn là trong lớp thành phần của bạn, bạn đã xác định một trường (thuộc tính)onToggleLoop = () => /*body using 'this'*/
gvlax

1

Nếu bạn đang sử dụng babel, bạn liên kết 'this' bằng cách sử dụng toán tử liên kết ES7 https://babeljs.io/docs/en/babel-plugin-transform-feft-bind#auto-elf-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

Nếu bạn gọi phương thức đã tạo của mình trong các phương thức vòng đời như thành phầnDidMount ... thì bạn chỉ có thể sử dụng hàm this.onToggleLoop = this.onToogleLoop.bind(this)và mũi tên chất béo onToggleLoop = (event) => {...}.

Cách tiếp cận thông thường của việc khai báo một hàm trong hàm tạo sẽ không hoạt động vì các phương thức vòng đời được gọi trước đó.


0

trong trường hợp của tôi đây là giải pháp = () => {}

methodName = (params) => {
//your code here with this.something
}

0

Trong trường hợp của tôi, đối với một thành phần không trạng thái đã nhận được ref với ForwardRef, tôi đã phải thực hiện những gì được nói ở đây https://itnext.io/reUSE-the-ref-from-forwardref-with-react-hooks-4ce9df693dd

Từ đây (onClick không có quyền truy cập tương đương với 'cái này')

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

Để điều này (nó hoạt động)

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} } />
})
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.