Реакція: "це" не визначено всередині функції компонента


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стан перемикання, але thisоб'єкт не визначений в обробнику. Згідно з підручником doc, я 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 у ваш клас реагування, вона також буде працювати.
Сем

71
Вам справді доводиться зв’язувати кожен метод кожного класу реагування? Хіба це не трохи шалено?
Алекс Л

6
@AlexL Є способи зробити це без явного прив'язки методів. якщо ви використовуєте babel, можна оголосити кожен метод компонента React функцією стрілки. Тут є приклади: babeljs.io/blog/2015/06/07/react-on-es6-plus
Іван

7
Але чому не thisвизначено в першу чергу? Я знаю, що thisв JavaScript залежить від того, як викликається функція, але що тут відбувається?
maverick

1
але як я можу це зробити? Функція не визначена до конструктора після закінчення? Я отримую "Не можу прочитати властивість" прив'язувати "невизначеного", якщо я спробую це зробити в конструкторі, навіть якщо ти моя функція визначена в класі :(
rex

87

Є кілька способів.

Одне - додати this.onToggleLoop = this.onToggleLoop.bind(this);в конструктор.

Інша - функції стрілок onToggleLoop = (event) => {...}.

А далі є onClick={this.onToggleLoop.bind(this)}.


1
Чому onToogleLoop = () => {} працює? У мене з'явилася та сама проблема, і я в'яжу її в своєму конструкторі, але вона не працює ... і тепер я побачив ваш пост і замінив свій метод синтаксисом функції стрілки, і він працює. Ви можете мені це пояснити?
Гучелкабен

5
від developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ; Функція стрілки не створює цього власного, використовується це значення контексту виконання, що додається.
Дж. Марк Стівенс

1
Зауважте, що прив'язка вбудованого в onClickзаповіті повертає нову функцію кожного рендеру, і, таким чином, схоже, що нове значення було передано для опори, заплутавшись shouldComponentUpdateв PureComponents.
Ninjakannon

25

Напишіть свою функцію таким чином:

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

Функції жирової стрілки

прив’язка для ключового слова це те саме зовні і всередині жирової стрілки. Це відрізняється від функцій, оголошених функцією, які можуть прив’язати це до іншого об'єкта при виклику. Підтримка цього прив'язки дуже зручна для таких операцій, як картографування: this.items.map (x => this.doSomethingWith (x)).


Якщо я це роблю, я отримую ReferenceError: fields are not currently supported.
Павло Комаров

Це працює, якщо всередині конструктора я скажу this.func = () => {...}, але я вважаю, що це якась гуфія і хочу, щоб уникнути цього, якщо можливо.
Павло Комаров

Настільки жахливо, що ти не можеш використовувати звичайний синтаксис класу в React!
Кокодоко

11

Я зіткнувся з подібним зв'язком thisу функції візуалізації і в кінцевому підсумку передав контекст наступним чином:

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

Я також використовував:

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

Це багато непотрібних функцій, які ви створюєте там, кожного разу, коли список відображається ...
TJ Crowder

@TJCrowder Так, ці функції створюються заново щоразу, коли викликається візуалізація. Краще створити функції як методи класу та прив’язати їх один раз до класу, але для початківців корисне контекстне прив’язка може бути корисною
duhaime

2

Ви повинні помітити, що thisзалежить від того, як функція викликається, тобто: коли функція викликається як метод об'єкта, thisвона встановлюється об'єкту, метод викликається.

thisдоступний у контексті JSX як ваш компонентний об'єкт, тож ви можете викликати потрібний метод inline як thisметод.

Якщо ви просто передаєте посилання на функцію / метод, здається, що react буде викликати його як незалежну функцію.

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'*/
gvlax

1

Якщо ви використовуєте babel, ви пов'язуєте "це" за допомогою оператора зв'язування 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

Якщо ви називаєте створений метод таким способом життєвого циклу, як компонентDidMount ..., ви можете використовувати лише функцію this.onToggleLoop = this.onToogleLoop.bind(this)та жирову стрілку onToggleLoop = (event) => {...}.

Нормальний підхід до оголошення функції в конструкторі не працює, оскільки методи життєвого циклу називаються раніше.


0

у моєму випадку це було рішення = () => {}

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

0

У моєму випадку для компонента без громадянства, який отримав посилання з forwardRef, я повинен був зробити те, що тут сказано https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd

З цього (onClick не має доступу до еквівалента "це")

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} } />
})
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.