React: componentDidReceiveProps Пожалуйста

Созданный на 27 февр. 2015  ·  94Комментарии  ·  Источник: facebook/react

Я хотел бы смиренно запросить хук componentDidReceiveProps, часто я хотел бы что-то сделать как с componentWillMount, так и с componentWillReceiveProps, но поскольку this.props еще не установлен, я вынужден передавать реквизиты вместо чтения непосредственно из this.props .

Перед новым крючком

componentWillMount() {
  this.setup(this.props.id);
}

componentWillReceiveProps(next) {
  this.setup(next.id);
}

setup(id) {
  UserActions.load(id);
}

После нового крючка

componentWillMount() {
  this.setup();
}

componentDidReceiveProps() {
  this.setup();
}

setup() {
  UserActions.load(this.props.id);
}

В этом простом примере это может показаться мелочью, но часто передача props идет глубоко, и вместо удобной ссылки на this.props приходится размещать props по всему компоненту.

Пожалуйста, подумайте о добавлении componentDidReceiveProps в качестве ловушки для использования того же кода, который используется в componentWillMount, без принуждения обоих к вертикальным опорам по всему компоненту.

Самый полезный комментарий

@syranide Проблема в том, что при установке необходимо вызвать методы, которым также нужны реквизиты, которые должны вызывать методы, которым также нужны реквизиты ... В конце концов, весь ваш компонент вращается вокруг реквизита.

Все 94 Комментарий

Почему не setup(props) ?

Просматривая свои собственные проекты, я вижу, что сделал это там, где у меня были аналогичные потребности (другой - получение фрагмента состояния на основе реквизита), просто передавая другой набор реквизитов, когда это необходимо, поэтому вам нужно только передать что-то дополнительное. при повторном использовании, без дублирования информации о том, какие реквизиты необходимы:

setup(props) {
  props = props || this.props
  UserActions.load(props.id)
}

@syranide Проблема в том, что при установке необходимо вызвать методы, которым также нужны реквизиты, которые должны вызывать методы, которым также нужны реквизиты ... В конце концов, весь ваш компонент вращается вокруг реквизита.

+1 кажется кучей ненужных подключений в приложении с текущим шаблоном. Может быть краткий стандартизированный способ решения проблемы. Видно, как кучу людей жгли на this.props внутри ComponentWillReceiveProps, что является явным признаком того, что это не интуитивно понятно.

+1, меня это тоже расстраивает.

Больше не считаю это проблемой.

+1, я также обнаружил, что часто передаю реквизит. Как и в случае с

setup(props = this.props) {
  doSomething(props);
}

Но решил, что это анти-шаблон из-за тонких ошибок, которые он может вызвать, если вы забудете передать newProps.

+1

Я думаю, причина, по которой он недоступен, заключается в том, что this.props и this.state _always_ соответствуют отображаемым значениям. Невозможно реализовать componentDidReceiveProps , который не запускал бы другой рендеринг, без нарушения этого ограничения.

В большинстве случаев, если вы используете componentWillReceiveProps , вам действительно нужен либо компонент более высокого порядка а-ля Relay , либо что-то вроде хука observe предложенного для React 0.14 .

+1
Кроме того, вы можете реализовать componentDidReceiveProps без изменения this.props или this.state . Если все, что вам нужно сделать, это прочитать из них, вы не будете запускать другой рендер. Если вы пишете в props или состояние в рамках этого предложенного вызова функции, то вы стреляете себе в ногу, но это то же самое для любого другого метода в жизненном цикле.

+1

Я хочу иметь возможность реагировать на новое событие props, когда shouldComponentUpdate возвращает false , поэтому в моем случае я не могу использовать componentDidUpdate .

+1

+1

А как насчет componentWillMount и componentWillUpdate @iammerrick

Я хочу иметь возможность реагировать на новое событие props, когда shouldComponentUpdate возвращает false, поэтому в моем случае я не могу использовать componentDidUpdate.

Использовать componentWillReceiveProps ?

+1

+1 это помогает с СУХИМ кодом и упрощает логику.

Я был бы открыт для вызова componentDidReceiveProps при начальной настройке, чтобы в примере, запускающем этот поток, была всего одна функция:

componentDidReceiveProps() {
  UserActions.load(this.props.id);
}

Мысли?

componentWillReceiveProps() {
  setTimeout(()=> {
    if(this.isMounted()) {
      this.componentDidReceiveProps();
    }
  });
}
componentDidReceiveProps() {
  UserActions.load(this.props.id);
}

Что вы думаете?

@YourDeveloperFriend Я думаю, это зависит от того, как он работает относительно других хуков жизненного цикла и задержек рендеринга из-за каскадных тайм-аутов вложенных компонентов.

Перехватчики жизненного цикла такого типа должны вызываться синхронно в проходе, который гарантированно вызывается перед рендерингом. Я не изучал кодовую базу React, но предполагаю, что рендеринг не вызывается по таймауту.

Скорее всего, лучшим решением будет что-то вроде регистрации хуков предварительного рендеринга или пометки компонентов, у которых были изменены их свойства, чтобы логика рендеринга вызывала componentDidReceiveProps перед вызовом рендеринга.

+1 пожалуйста. Так некрасиво.

Нет, я в порядке. Есть решения получше.

+1

componentDidReceiveProps() уже существует: он называется render() . Однако может быть случай (как в примере @iammerrick ), когда вам нужно что-то загрузить / запустить / и т.д. перед выполнением рендеринга. Это означает одно и единственное: вы делаете что-то не так.

Когда ты делаешь что-то вроде

setup(id) {
    UserActions.load(id);
}

вы вводите statefullness либо внутри компонента, либо (что еще хуже) снаружи. Зачем вам когда-либо нужно load() data каждый раз, когда компонент получает реквизиты (даже не гарантируется, что они новые )? Если вы выполняете отложенную загрузку, правильный способ - запросить данные в методе render() :

render() {
    var actions = UserActions.load(id);
    if (actions) // render
    else // show spinner or return null, etc.
}

UserActions.load = function(id) {
    if (data) return data;
    else // request the data, emit change on success
}

@robyoder , передача аргументов функциям - это не уродливо. Это может показаться слишком многословным, но это естественно для выбранного вами языка программирования. Уничтожать API, добавляя лишний метод жизненного цикла только для уменьшения многословности, определенно некрасиво.

Так скажи мне, @ me-andre, почему у нас есть и componentWillUpdate и componentWillReceiveProps ?

На мой взгляд, это потому, что они служат разным целям, и обе полезны. Точно так же, как эти два не совпадают, render - это не то же самое, что и гипотетический componentDidReceiveProps потому что он вызывается по причинам, отличным от новых свойств; Кроме того, вам не предоставляется доступ к предыдущим реквизитам.

Преимущество предыдущих свойств заключается в том, что вам не придется загружать данные каждый раз; он может быть условным, в зависимости от свойств и от того, изменились ли они.

Очевидно, «[уродство] в глазах смотрящего», потому что добавление правильного метода жизненного цикла кажется мне гораздо более чистым решением.

Может быть, есть другой способ взглянуть на это ...

Допустим, основные цели здесь следующие:

(1) уменьшить количество человеческих ошибок - я забыл передать параметры или снова использовать props = props || this.props
(2) уменьшить ненужный добавочный шаблонный код - зачем писать что-то без надобности
(3) уменьшить когнитивную нагрузку на попытки решить, какие методы жизненного цикла использовать - зачем мне думать о таких вещах, почему я продолжаю использовать несколько разные подходы в зависимости от того, как я себя чувствую в тот день, и т. Д.

Так что, возможно, область решений связана с упрощением использования React и самого React API для достижения этих целей.

Если вы думаете о проблеме с учетом этих целей, возможно, некоторые методы жизненного цикла можно объединить, и если вам интересно знать, что это инициализация против обновления, вы можете сказать это по вызывающей сигнатуре / аргументам. Например: beforeRender(prevProps, prevState) или update(prevProps, prevState) .

Лично мне кажется, что существует слишком много методов жизненного цикла, и если бы они вызывались более последовательно (без или с prevProps / prevState при начальном проходе и обновлении), это могло бы упростить мой код и повысить мою продуктивность. Кроме того, имена методов жизненного цикла довольно длинные (я обычно копирую / вставляю их, а не набираю), и трудно вспомнить, какие из них будут / существовали, что заставляет меня думать, что их можно / нужно упростить.

@robyoder , краткий ответ, почему у нас есть и componentWillUpdate и componentDidReceiveProps заключается в том, что между ними огромная разница. Они, конечно, похожи, но в основном имеют префикс componentWill .

| | состояние изменилось | компонент не обновился |
| --- | --- | --- |
| componentWillUpdate () | да | нет |
| componentWillReceiveProps () | нет | да |

Как вы могли заметить, существуют точки / условия жизненного цикла, когда один метод вызывается, а другой - нет. Вот почему у нас есть 2 разных метода жизненного цикла.

Однако componentDidReceiveProps() как это было описано в этом разделе, не представляет состояние или условие какого-либо компонента и не дает доступа к чему-либо, чего не делает componentWillReceiveProps() . Он просто добавляет небольшой синтаксический сахар, который может показаться полезным или простым - а может и нет - это личное предпочтение, а не техническое требование . И это как раз причина, по которой его не следует добавлять: это субъективно. Вы говорите, что передавать аргументы некрасиво, но вы также сказали, что «[уродство] в глазах смотрящего». Мне кажется, вы сами ответили.

@ me-andre, возможно, вы отвечаете мне (@kmalakoff) и @robyoder одновременно. Или, может быть, нет, но я воспользуюсь этой возможностью, чтобы продвинуть обсуждение вперед ...

Я говорил о том, что, возможно, размышления над текущим API с этими тремя вышеупомянутыми целями могут дать новое понимание или перспективы того, как можно упростить API для их достижения. Конечно, после выполнения упражнения мы можем оказаться в одном и том же месте.

Проделаем упражнение ....

Предположим, что за этой темой следят и добавили +1, потому что в ней есть что-то важное, и предположим, что добавление componentDidReceiveProps не является хорошей идеей, потому что это увеличивает площадь поверхности API.

Принимая во внимание таблицу и 3 цели, которые я выдвинул, есть ли у кого-нибудь идеи для упрощения API и / или другого способа дать людям в этом потоке то, что они хотят, и не расширять API (возможно, даже уменьшить / упростить его )?

@kmalakoff , 3 пункта, о которых вы говорите, связаны с API только тем, что вы используете API, когда сталкиваетесь с ними. Они не вызваны плохим дизайном.

Первая проблема, о которой вы говорите, заключается в том, что методы жизненного цикла принимают разные аргументы. Что ж, это совершенно естественно - они служат разным целям. componentWillReceiveProps принимает реквизиты не потому, что была полная луна, когда этот метод был добавлен, а потому что он касается получения реквизитов, которые еще не были назначены компоненту. props передаются только в методах, где они (могут) отличаться от this.props . На самом деле это намек, а не проблема.
Проблема №3 в том, что вам сложно решить, какой обратный вызов / подход использовать. Что ж, это не проблема, пока методы, о которых я говорю выше, не будут иметь одинаковую подпись. Как только у вас есть state и this.state , props и this.props которые равны (читать бессмысленно) в большинстве методов, тогда вы запутаетесь в запасных опциях и альтернативы.
Проблема №2 связана с шаблонным кодом ... ну, React - тонкая библиотека, и поэтому она красивая, простая и строгая. Если вы хотите создать оболочку, которая упростила бы нашу жизнь и уменьшила объем кода, который мы пишем каждый день - почему бы не сделать это? Опубликуйте его как свой собственный npm который зависит от react и все готово.

Что касается «слишком много методов жизненного цикла» - пока нет и никогда не будет, если вы перестанете запрашивать новые обратные вызовы, в которых у вас нет технической необходимости. Техническая потребность при реализации комплексного компонента и в середине работы , вы понимаете, нет абсолютно никакого способа сделать это без какого - либо уродливых взломать. Речь идет о том, чтобы «вызвать хитрый метод DOM, мне нужно что-то вызвать в то время, когда компонент выполняет X, но такого метода нет, и я не могу его добавить, поэтому я использую setTimeout и, надеюсь, он не приземлится раньше, чем мне нужно». Не тогда, когда вы говорите: «О, я устал писать props = this.props».

И вот еще...
В хорошо спроектированном приложении React вам не нужно большинство методов, о которых мы говорим, или вы используете их очень редко. getInitialState , componentWillMount , componentWillUnmount достаточно в 99% случаев. В моем текущем проекте, который представляет собой относительно большое коммерческое приложение, componentWillReceiveProps используется дважды во всем приложении. Я бы вообще не стал его использовать, но среда (читай браузер) несовершенная - манипулирование некоторыми вещами, имеющими "состояние в себе", такими как scrollTop или расчетом макета для будущего render s требует ручной синхронизации между переходами props и изменениями DOM. Однако в большинстве «нормальных» случаев вам просто не нужно знать, когда происходит переход props .

@ me-andre основываясь на моем опыте работы с React, я считаю, что есть возможности для улучшения / упрощения API. Я не одинок, поэтому этот вопрос был поднят в первую очередь и получил +1 к изданию.

Если я отвечу, предоставив обратную связь по вашему анализу, я не думаю, что он действительно продвинется вперед (вот почему я до сих пор избегал этого), поскольку он немного предвзято относится к статус-кво и оправдывает текущий API, но Буду рад поделиться своим мнением о возможных решениях! Люди, занимающиеся этой проблемой, стремятся изучить идеи по улучшению и возможные изменения API, чтобы решить проблемы, возникшие из-за их опыта использования React (как я описал выше).

Возможно, вы правы в том, что сообществу следует внести новшества, чтобы продвинуть API вперед и, возможно, откатить его обратно в ядро, или, может быть, текущий API идеален как есть, но я думаю, что это отличная возможность подумать, как могут выглядеть изменения с команда React.

Возможно, причина, по которой вы так аргументируете, заключается в том, что мы просто пользователи библиотеки с меньшей глубиной понимания текущего API и принятых решений. Может быть, вы могли бы привлечь других членов команды React, которые столь же опытны и осведомлены, как и вы, чтобы посмотреть, получим ли мы другую точку зрения и, может быть, даже придумаем отличное решение или достигнем консенсуса, что это настолько хорошо, насколько это возможно?

@kmalakoff , представьте, что React - это двигатель и 4 колеса. Теперь каждый раз, когда вы хотите куда-то поехать, вы строите остальную часть машины, и это в конечном итоге начинает раздражать. В противном случае вы будете жаловаться на необходимость запоминать мелкие детали двигателя, а поворот передних колес руками - это неправильный путь.
Я бы порекомендовал либо получить законченное транспортное средство (полноценный фреймворк), либо создать необходимые компоненты таким образом, чтобы их можно было повторно использовать с течением времени.
В этой ветке я вижу, что пользователи двигателей жалуются, что в двигателе нет гидравлической системы и внутреннего освещения. Я чувствую, что мы получим полную чушь, если добавим эти функции туда, где им не место.
React - это не фреймворк: это скорее движок рендеринга, основанный на различиях, который предоставляет мощную компонентную систему. Он имеет низкоуровневый и минималистичный API, но при этом достаточно мощный, чтобы вы могли создавать буквально все на его основе.
Пожалуйста, не стесняйтесь обращаться ко мне по электронной почте, если вы чувствуете, что мне нужно что-то уточнить - я не хочу, чтобы эта ветка стала учебным пособием.

@ me-andre Я считаю, что теперь мы хорошо понимаем вашу позицию и вашу аргументацию. Спасибо! Возможно, вы правы в том, что API уже настолько хорош, насколько это возможно, но есть вероятность, что его можно улучшить.

Может ли кто-нибудь обосновать необходимость изменения API? например. обеспечение сравнительного анализа различных вариантов (добавить componentDidReceiveProps против слияния / упрощения API против без изменений)

Команда React наблюдала за этой проблемой, и мы ее обсуждаем внутри компании. В принципе, я всегда склоняюсь к исключению площади поверхности API, поэтому я склоняюсь к аргументам @me-andre. Однако наша команда иногда с любовью относится к componentDidReceiveProps как к «отсутствующему методу жизненного цикла», и ведутся серьезные дискуссии о его возможном добавлении. Важно то, что мы внедряем передовые методы, не усугубляя плохие практики.

Для нас (или, по крайней мере, для меня) было бы наиболее полезно иметь твердое представление о том, ПОЧЕМУ людям нужен этот метод жизненного цикла. Что вы пытаетесь сделать в этом жизненном цикле, который недостаточно охвачен другими методами жизненного цикла? Зачем кому-то нужно делать UserActions.load(id); в componentDidReceiveProps вместо рендеринга, как предлагается в https://github.com/facebook/react/issues/3279#issuecomment -163875454 (я могу придумать причина, но мне любопытно, каковы ваши причины)? Существуют ли какие-либо другие варианты использования, кроме запуска загрузки данных на основе props?

@jimfb Я считаю, что componentDidReceiveProps - это именно то, что мне нужно, и что другие методы не подходят, но я могу ошибаться. Я с радостью объясню свой вариант использования.

У меня есть маршрут в моем react-router который выглядит так:

    <Route path="/profile/:username" component={ProfilePage} />

Мне нужно получить данные для профиля из внешнего источника, и для того, чтобы сделать это правильно, мне нужно сделать HTTP-запрос в методе componentDidMount .

К сожалению, когда я перехожу из одного профиля в другой, React Router не вызывает снова метод конструктора или метод componentDidMount (конечно, это может быть ошибкой, но я предполагаю, что это не сейчас) .

Я подумал о том, чтобы исправить это, используя теоретический componentDidReceiveProps . К сожалению, его еще нет, и componentWillReceiveProps мне не подходит.

Будем очень признательны за любые указатели на это.

Я догадываюсь, что componentDidReceiveProps - это именно то, что мне нужно.

@ shea256 Не могли бы вы объяснить, почему componentWillReceiveProps не отвечает вашим потребностям?

@ shea256 Кроме того, почему ваш компонент должен делать HTTP-запрос? Что содержит этот HTTP-запрос? Если данные, почему вы не обновляете свой компонент асинхронно, когда возвращается обратный вызов данных?

@jimfb Обычный случай для меня - это когда мы должны загружать что-то асинхронно в ответ на изменение свойства, которое включает в себя установку некоторого состояния, указывающего, что происходит загрузка, а затем, когда данные загружаются, устанавливая это состояние.

И установка, и получение новых реквизитов должны запускать один и тот же цикл загрузки, поэтому componentDidMount и componentWillReceiveProps - это место для вызова функции обновления. render не имеет информации о том, является ли это новой опорой, и в любом случае setState из рендеринга - нет-нет.

Итак, у меня есть одна функция, которая выполняет загрузку. Но я должен передать props в качестве параметра и осторожно избегать использования this.props которые устарели для componentWillReceiveProps . Это неизбежно приводит к ошибкам, так как вы должны помнить об использовании пропсов, переданных в props, иначе при поступлении изменений вы получите незаметные ошибки «один позади». И сигнатуры всех задействованных методов более сложны, поскольку необходимо передавать реквизиты.

Да, это можно сделать с текущим API. Но это вызывает более неуклюжий, подверженный ошибкам код, чего React так хорошо избегает в целом.

@jimfb Мне нужно получить новое имя пользователя в routeParam а с componentWillReceiveProps меня его еще нет.

Мне нужно сделать HTTP-запрос, чтобы загрузить данные профиля из внешнего источника (данные не хранятся локально).

И я могу обновить свой компонент в методе рендеринга, но он кажется немного грязным:

constructor(props) {
  super(props)

  this.state = {
    id: this.props.routeParams.id,
    profile: {}
  }
}

componentDidMount() {
  this.getProfile(this.state.id)
}

render() {
  if (this.props.routeParams.id !== this.state.id) {
    this.getProfile(this.props.routeParams.id)
  }

  return (
    <div>
      <div className="name">
       {this.state.profile.name}
      </div>
    </div>
  )
}

@grassick написал:

Обычный случай для меня - это когда мы должны загружать что-то асинхронно в ответ на изменение свойства, которое включает в себя установку некоторого состояния, указывающего, что происходит загрузка, а затем, когда данные загружаются, устанавливая это состояние.

Да, это как раз мой вариант использования.

И установка, и получение новых свойств должны запускать один и тот же цикл загрузки, поэтому componentDidMount и componentWillReceiveProps - это место для вызова функции обновления.

Хорошо сказано.

@ shea256 нельзя это сделать, используя вместо этого componentWillReceiveProps ?

constructor(props) {
  super(props)

  this.state = {
    id: this.props.routeParams.id,
    profile: {}
  }
}

componentDidMount() {
  this.getProfile(this.state.id)
}

componentWillReceiveProps(nextProps) {
  let { id } = nextProps.params
  if(id !== this.state.id) {
    this.getProfile(id, (profile) => {
      this.setState({ id: id, profile: profile })
    })
  }
}

render() {
  return (
    <div>
      <div className="name">
       {this.state.profile.name}
      </div>
    </div>
  )
}

@grassick написал:

Обычный случай для меня - это когда мы должны загружать что-то асинхронно в ответ на изменение свойства, которое включает в себя установку некоторого состояния, указывающего, что происходит загрузка, а затем, когда данные загружаются, устанавливая это состояние.

@grassick написал:

render не имеет информации о том, является ли это новой опорой, и в любом случае setState из render - нет-нет.

Просто наплевать: если componentDidUpdate вызван при первоначальном рендеринге (в дополнение к последующим рендерингам), решит ли это ваш вариант использования? Вы можете проверить, изменились ли реквизиты, и загрузить все ваши данные в componentDidUpdate , верно? А при рендеринге вы бы знали, что загружаете данные, если бы this.state.profileName != this.props.profileName . Достаточно ли этой альтернативы для ваших сценариев использования?


Существуют ли какие-либо варианты использования, которые НЕ связаны с загрузкой данных компонентов на основе свойств?

@calmdev хм, я думаю, ты прав. Я попробую.

Возможно, @jimfb , хотя я пробовал использовать componentDidUpdate и думал, что это не сработало. Я могу еще раз взглянуть.

И похоже, что я вполне мог бы проверить this.state.profileName != this.props.profileName но это тоже похоже на взлом, не так ли? На этом этапе, если componentWillReceiveProps(nextProps) закончит работать, я бы предпочел это. Что меня беспокоит, так это отсутствие симметрии с componentDidMount . Могу ли я использовать componentWillMount вместо componentDidMount ?

Я просто чувствую, что весь жизненный цикл может быть чище.

@ shea256 , у меня к вам вопрос ... вы читали README для React?
Мне неудобно говорить это, но если нет, возможно, вам не следует запрашивать новые функции для инструмента, с которым вы не знакомы. Для меня это даже звучит ... абсурдно.
"Могу ли я использовать componentWillMount вместо componentDidMount ?"
Нет, вы не можете, потому что, как указано в readme, componentWillMount вызывается до того, как ваш компонент попадает в DOM, а componentDidMount - после этого. Что ж, вы определенно можете заменить один метод другим, и это просто нарушит ваш код. Причина, по которой у нас есть 2 метода, не в эстетике (читай «симметрия»). Это потому, что нам может понадобиться один метод для подготовки перед рендерингом, а другой - для некоторых начальных запросов / манипуляций с DOM после первого рендеринга. Но даже это экзотично для среднестатистического компонента React. Обычно вам просто не нужно обращаться к DOM вручную.
«Похоже, я вполне мог бы проверить this.state.profileName! = This.props.profileName, но это тоже похоже на взлом, не так ли?»
Да, весь ваш подход - это взлом. И кто сказал вам, что componentDidReceiveProps гарантирует, что props были изменены? Ничто никогда не сделает этого, если вы не определите shouldComponentUpdate .
Именно так работает React.

@ me-andre, спасибо, что прыгнули, но на мой вкус ты слишком резок. Также я не верю, что вы поняли мои вопросы. Я хорошо знаком с тем, что делают componentWillMount и componentDidMount . Подожду ответа

@ shea256 ,
«Также я не верю, что вы поняли мои вопросы».
Не могли бы вы указать, где я упускаю главное, отвечая на ваши вопросы?
Кроме того, не могли бы вы пояснить, о чем вы пытаетесь спросить.
Кроме того, мы здесь не для того, чтобы обсуждать личности или вкусы. Это не личный разговор и не техподдержка. Это даже не веб-сайт обмена стеками, на котором можно было бы ожидать, что он проведет вас через некоторые области знаний.
Я часто говорю на местных встречах, а также на международных конференциях о React (и не только), и я очень открыт для обмена знаниями - когда и где это уместно. Вы всегда можете связаться со мной напрямую по электронной почте или по скайпу.

По поводу вашей проблемы с загрузкой данных профиля. Вы пытаетесь решить проблему применения классического императивного подхода к функционально-ориентированному фреймворку. Представление в React - это функция свойств, состояния и среды. Как function(state, props) { return // whatever you've computed from it } (но в реальном мире все становится немного сложнее - иначе React вообще не существовал бы). Хотя в 0.14 мы получаем чисто функциональные компоненты, для большинства компонентов эта функция входа - render() .

Это означает, что вы начинаете писать с render() и предполагаете, что ваши реквизиты всегда актуальны, и вам все равно, были ли изменены props , сколько раз и где . Ваш случай может быть реализован следующим образом:

// profile-view.js

var React = require('react');

module.exports = React.createClass({
    contextTypes: {
        app: React.PropTypes.object.isRequired
        // another option is var app = require('app')
        // or even var profiles = require('stores/profiles')
    },
    componentWillMount() {
        var {app} = this.context; // another option is to require('app')
        app.profiles.addListener('change', this.onStoreChange);
    },
    componentWillUnmount() {
        var {app} = this.context; // another option is to require('app')
        app.profiles.removeChangeListener('change', this.onStoreChange);
    },
    onStoreChange() {
        this.forceUpdate();
    },
    render() {
        var {app} = this.context;
        var profile = app.profiles.read(this.props.routeParams.id);
        if (profile) { // profile's been loaded
            return <div>{profile.name}</div>;
        } else { // not yet
            return <div>Loading...</div>;
        }
    }
});

// profiles-store.js
// app.profiles = new Profiles() on app initialization

var EventEmitter = require('events');
var {inherits} = require('util');

module.exports = Profiles;

function Profiles() {
    this.profiles = {};
}

inherits(Profiles, EventEmitter);

Profiles.prototype.read = function(id) {
    var profile = this.profiles[id];
    if (!profile) {
        $.get(`profiles/${id}`).then(profile => {
            this.profiles[id] = profile;
            this.emit('change');
        });
    }
    return profile;
};

Довольно просто.
И вам не нужно componentDidReceiveProps . Вам даже не понадобятся componentWillReceiveProps и подобные крючки. Если вы когда-нибудь почувствуете, что они вам нужны в тривиальном случае, вам не хватает понимания того, как что-то делать в React. Для того , чтобы получить его , пожалуйста , используйте соответствующие ресурсы, не только мусор Issues раздел хранилища. На мой вкус это кажется слишком резким. Такое чувство, что ты не уважаешь чужое время.

@ me-andre Вероятно, стоит немного смягчить свой язык, так как он может показаться немного конфронтационным, даже если вы просто пытаетесь помочь. Мы хотим создать гостеприимное сообщество для всех; мы все когда-то были новичками. Несмотря на то, что вы правы в отношении некоторых своих замечаний по API / дизайну, сам факт того, что так много людей высказывают +1 к проблеме, указывает на то, что что-то не так, поэтому мы должны попытаться понять, что / почему люди хотят этого жизненный цикл. Возможно, проблема в том, что мы недостаточно сообщаем о том, как правильно писать компоненты (документацию), или, возможно, API React нуждается в изменении - в любом случае стоит разобраться в жалобах / вопросах / комментариях здесь.

@ shea256 this.state.profileName != this.props.profileName проверяет, соответствует ли внутреннее состояние (то, что компонент отображает) тому, что родитель запрашивает у компонента. Это почти определение «компонент обновляется» или «компонент обновлен», поэтому я не считаю это хакерским. По крайней мере, не более хакерской, чем идея «запустить запрос на обновление данных при изменении свойства и выполнить setstate в обратном вызове» - это в первую очередь.

@ shea256 Для ясности: этот предлагаемый жизненный цикл не позволит вам делать то, что вы не могли бы сделать сегодня с текущими жизненными циклами. Это просто сделало бы его потенциально более удобным. Как уже упоминали другие, то, что вы пытаетесь сделать, возможно с текущими жизненными циклами (существует несколько комбинаций жизненных циклов, которые позволят вам достичь вашей цели). Если вы пытаетесь «заставить его работать», то лучше обсудить это на StackOverflow. Эта проблема с github пытается понять необходимость componentDidReceiveProps поскольку это связано с эргономикой разработчика.

И кто сказал вам, что componentDidReceiveProps гарантирует, что реквизиты были изменены?

@ me-andre здесь прав. Большинство методов жизненного цикла на самом деле не гарантируют, что что-то изменилось; они только указывают на то, что что-то _might_ изменилось. По этой причине вам всегда нужно проверять, есть ли previous === next если вы собираетесь сделать что-то вроде HTTP-запроса. Группа людей в этой ветке, кажется, делает предположение, что их ценность изменилась только потому, что сработал метод жизненного цикла.

@ me-andre написал:

Вы пытаетесь решить проблему применения классического императивного подхода к функционально-ориентированному фреймворку.

Я думаю, что это может быть основной причиной того, что людям нужен этот новый метод жизненного цикла, но я все еще пытаюсь понять, почему и как люди хотят использовать этот метод. Вполне возможно, что текущая семантика жизненного цикла немного не соответствует тому, что люди обычно хотят делать.

Все: есть ли какие-либо варианты использования для componentDidReceiveProps кроме "асинхронной загрузки данных в ответ на изменение свойства"?

cc @grassick @iammerrick

@jimfb Я просмотрел свой код и также использую componentWillReceiveProps для создания дорогостоящих в создании объектов, необходимых для рендеринга. Этот случай страдает той же проблемой, что он должен передавать реквизиты и быть осторожным, чтобы не использовать this.props в коде.

@jimfb написал:

@ me-andre Вероятно, стоит немного смягчить свой язык, так как он может показаться немного конфронтационным, даже если вы просто пытаетесь помочь. Мы хотим создать гостеприимное сообщество для всех; мы все когда-то были новичками. Несмотря на то, что вы правы в отношении некоторых своих замечаний по API / дизайну, сам факт того, что так много людей высказывают +1 к проблеме, указывает на то, что что-то не так, поэтому мы должны попытаться понять, что / почему люди хотят этого жизненный цикл. Возможно, проблема в том, что мы недостаточно сообщаем о том, как правильно писать компоненты (документацию), или, возможно, API React нуждается в изменении - в любом случае стоит разобраться в жалобах / вопросах / комментариях здесь.

Спасибо, что упомянули об этом. Очень важно убедиться, что у вас приветливое сообщество, и мое общение с вами было только приятным.

@jimfb написал:

@ shea256 this.state.profileName! = this.props.profileName проверяет, соответствует ли внутреннее состояние (то, что визуализирует компонент) тому, что родитель запрашивает у компонента для визуализации. Это почти определение «компонент обновляется» или «компонент обновлен», поэтому я не считаю это хакерским. По крайней мере, не более хакерской, чем идея «запустить запрос на обновление данных при изменении свойства и выполнить setstate в обратном вызове» - это в первую очередь.

Да, это кажется разумным.

@jimfb написал:

@ shea256 Для ясности: этот предлагаемый жизненный цикл не позволит вам делать то, что вы не могли бы сделать сегодня с текущими жизненными циклами. Это просто сделало бы его потенциально более удобным. Как уже упоминали другие, то, что вы пытаетесь сделать, возможно с текущими жизненными циклами (существует несколько комбинаций жизненных циклов, которые позволят вам достичь вашей цели). Если вы пытаетесь «заставить его работать», то лучше обсудить это на StackOverflow. Эта проблема с github пытается понять необходимость componentDidReceiveProps, поскольку она связана с эргономикой разработчика.

Я полностью с этим согласен. Я не публиковал свой конкретный вариант использования, чтобы получить по нему помощь. Я написал, чтобы дать некоторое представление о том, почему я подумал, что componentDidReceiveProps будет полезно.

У меня и еще более десятка других людей, которые разместили сообщения, явно было желание использовать что-то подобное (что означает, что их, вероятно, еще сотни или тысячи), что указывает мне на то, что API не так интуитивно понятен, как мог бы быть.

@jimfb написал:

@ me-andre здесь прав. Большинство методов жизненного цикла на самом деле не гарантируют, что что-то изменилось; они только указывают на то, что что-то могло измениться. По этой причине вам всегда нужно проверять, есть ли предыдущий === следующий, если вы собираетесь сделать что-то вроде HTTP-запроса. Группа людей в этой ветке, кажется, делает предположение, что их ценность изменилась только потому, что сработал метод жизненного цикла.

Я не делаю этого предположения. Я пропустил чек, но теперь использую его. Но в худшем случае сработает дополнительный запрос.

@jimfb написал:

Я думаю, что это может быть основной причиной того, что людям нужен этот новый метод жизненного цикла, но я все еще пытаюсь понять, почему и как люди хотят использовать этот метод. Вполне возможно, что текущая семантика жизненного цикла немного не соответствует тому, что люди обычно хотят делать.

Возможно. Я подумаю об этом подробнее.

componentDidReceiveProps было бы полезно для конкретного случая использования, который у меня есть.

Я использую архитектуру ReactRouter и Flux. Когда мой компонент создается, я устанавливаю свое состояние на элемент из магазина. Когда это хранилище генерирует событие изменения, я обновляю свое состояние, используя тот же поисковый запрос.

Когда мои реквизиты меняются из-за того, что я перешел к другому идентификатору элемента в том же маршруте, мне нужно снова запросить мой магазин, иначе мой компонент застрянет в состоянии из предыдущего элемента в магазине.

В настоящее время, когда вызывается componentWillReceiveProps , я проверяю, изменились ли какие-либо из моих параметров маршрута, и если они изменились, я вызываю updateItemState. Но поскольку свойства на самом деле еще не изменились, теперь я должен передать свойства в свой метод.

this.updateItemState( nextProps );

updateItemState( props ) {
    props = props || this.props;

    this.setState( {
        item: this.getItemState( props )
    } );
}

getItemState( props ) {
    props = props || this.props;

    return this.ItemStore.get( props.params[ this.paramName ] );
}

просто стал бы

this.updateItemState();

updateItemState() {
    this.setState( {
        item: this.getItemState()
    } );
}

getItemState() {
    return this.ItemStore.get( this.props.params[ this.paramName ] );
}

Я считаю, что это разумный вариант использования.

@ akinnee-gl et al: Если componentDidUpdate запускается после первоначального монтирования в дополнение к обновлениям, решит ли это ваш вариант использования?

@ akinnee-gl, если вы используете Flux, вам не нужно устанавливать состояние из магазина. Если это абсолютно невозможно, вы должны поддерживать состояние в одном месте. Когда дело доходит до Flux, этим местом является сам магазин. Когда магазин выдает изменение, вы просто forceUpdate() а затем в render() вы read() ваш магазин.

«Когда мои реквизиты меняются из-за того, что я перешел к другому идентификатору элемента в том же маршруте, мне нужно снова запросить мой магазин, иначе мой компонент застрянет с состоянием из предыдущего элемента в магазине».

Никогда.

Взгляните: вам нужно отобразить конкретный элемент из магазина, и какой элемент визуализировать, вы выбираете из props . Если это ваше требование, это должно быть выражено в коде так же, как и словами:

    var item = this.props.store.read(this.props.id);
    return <div>{item.name}</div>;

Это ваша составляющая, не более того.
Чтобы поиграть с Flux, вы можете написать многоразовый компонент для привязки / отмены привязки к хранилищу:

<FluxWrapper store={store} component={Component} id={this.props.routeParams.id} />

var FluxWrapper = React.createClass({
    componentWillMount() {
        this.props.store.addListener('change', this.onStoreChange);
    },
    componentWillUnmount() {
        this.props.store.removeListener('change', this.onStoreChange);
    },
    onStoreChange() {
        this.forceUpdate();
    },
    render() {
        var Component = this.props.component;
        return <Component {...this.props} />;
    }
});

var Component = React.createClass({
    render() {
        var item = this.props.store.read(this.props.id);
        return <div>{item.name}</div>;
    }
});

Посмотрите, насколько крошечными могут стать ваши компоненты, если вы правильно используете React.

Однако, если ваши магазины тяжелые и вы не можете просто read() на каждые render() , тогда вам понадобится промежуточное ПО для кэширования. Это может быть либо компонент многократного использования (похожий на FluxWrapper ), либо промежуточное хранилище прокси, которое затеняет исходный метод read() . Прокси-хранилища просты и удобны - они могут не только кэшировать чтение, но и подавлять изменения, если изменение родительского хранилища не было важным или значимым для случая.
Это называется композицией. Вы должны предпочесть композицию наследованию или расширению функциональности или чему-то еще, потому что композиция масштабируется .
Как только у вас возникнет проблема, которую трудно решить с помощью одного компонента, рассмотрите возможность использования двух или более компонентов вместо того, чтобы усложнять существующие компоненты.

@jimfb , что касается моего тона: я не являюсь носителем английского языка и даже не имею регулярной практики говорения, поэтому иногда могу выбрать неправильные слова - я просто не чувствую, насколько они эмоционально звучат.

Интересный подход. Я буду иметь ввиду. Во всех официальных документах по React говорится, что ваши функции рендеринга должны быть чистыми (это означает, что вы должны обращаться только к реквизитам и состоянию в методах рендеринга). Все примеры Flux показывают состояние настройки из магазина.

@jimfb Проблема с componentDidUpdate в том, что он вызывается по другой причине, кроме изменения

@ akinnee-gl componentWillReceiveProps также может вызываться по причинам, отличным от изменения реквизита (см .: https://github.com/facebook/react/issues/3279#issuecomment-164713518), поэтому вы ДОЛЖНЫ проверить, чтобы увидеть если свойство действительно изменилось (независимо от того, какой жизненный цикл вы выберете). Кроме того, если вы действительно выполняете асинхронные данные, вы будете ждать следующего цикла событий, прежде чем ваш обратный вызов все равно будет запущен, поэтому я не думаю, что это будет иметь значение, если ваша функция будет вызвана непосредственно до / после вашего функция рендеринга, верно?

@jimfb Ну, на самом деле, если элемент, на который ссылается опора, уже находится в магазине, нет необходимости повторять рендеринг. Если его еще нет в магазине, мы можем показать экран загрузки. Так что рендерить дважды все равно не имеет смысла. Но я понимаю, о чем вы говорите.

@ me-andre Мне очень нравится ваш подход, когда вы просто читаете из магазина во время рендеринга. Кажется, это упрощает код. Я боюсь, что, делая это, вы потеряете некоторую эффективность. Вы теряете возможность управлять обновлением через shouldComponentUpdate, и вам потребуется добавить этот дополнительный компонент FluxWrapper, о котором вы упомянули. Есть мысли по этому поводу?

Я следил за обсуждением и хотел бы снова вспомнить третий вариант: (1) API отличный как есть, (2) ему нужен componentDidReceiveProps, и (3), возможно, есть более простой API.

Я рассматриваю проблему как возможность увидеть, сможем ли мы решить основную потребность, вызванную этой проблемой, путем мозгового штурма более глубоких изменений API, а не ограничивая обсуждение пространством решений из (1) и (2).

Упрощенный API может быть основан на следующих принципах:

(A) упростить структуру кода / уменьшить шаблон
(B) храните реквизиты (входы) отдельно от состояния (внутренние)

Основная причина (A) в том, что я пишу шаблон, не добавляющий ценности. Пример:

componentWillMount() { this.actuallyCheckThePropsAndMaybeDoSomething(); }
componentWillReceiveProps(nextProps) { this.actuallyCheckThePropsAndMaybeDoSomething(nextProps); }

actuallyCheckThePropsAndMaybeDoSomething(props) {
  props = props || this.props;

  let relatedProps1 = _.pick(props, KEYS1);
  if (!shallowEqual(this.relatedProps1, relatedProps1) { // changed
   this.relatedProps1 = relatedProps1;

   // do something
  }

  let relatedProps2 = _.pick(props, KEYS2);
  if (!shallowEqual(this.relatedProps1, relatedProps2) { // changed
   this.relatedProps2 = relatedProps2;

   // do something else
  }
}

Я бы предпочел сделать следующее и не иметь отдельный путь кода для первого раза vs изменено:

propsUpdated(prevProps) {
  if (!shallowEqual(_.pick(prevProps || {}, KEYS1), _.pick(this.props, KEYS1)) { // changed
   // do something
  }

  if (!shallowEqual(_.pick(prevProps || {}, KEYS2), _.pick(this.props, KEYS2)) { // changed
   // do something
  }
}

Что касается (B), основная проблема с componentDidUpdate, как было только что упомянуто, заключается в том, что вы можете запускать повторные вызовы метода, если вы устанавливаете состояние из-за изменений свойств, поскольку он вызывается как для свойств (входов), так и (внутреннего) состояния. Эти пути кода кажутся лучше разделенными, потому что реквизиты поставляются извне, а состояние устанавливается внутри, например. Я пробовал / рассматривал четвертую возможность updated(prevProps, prevState) (упрощенное имя для componentDidUpdate, поскольку меньшее количество методов жизненного цикла могло бы позволить более короткое именование) с использованием componentDidUpdate для уменьшения шаблона, но я немного недоволен потенциальным избыточным вторым update, и что на практике логика кажется довольно независимой.

Исходя из принципов дизайна (я уверен, что их больше!), Мне интересно, может ли что-то вроде propsUpdated и stateUpdated быть потенциальным третьим вариантом для этого обсуждения. ?

@kmalakoff Вам всегда нужно будет проверять, изменились ли реквизиты, поскольку мы никогда не сможем окончательно сказать вам, что реквизиты изменились / не изменились (потому что Javascript не имеет типов значений и имеет изменяемость).

В этой заметке, возможно, он мог бы вызвать shouldComponentUpdate, чтобы сказать, должен ли он вызывать propsDidChange или что-то еще. Хотя отдельная проблема.

@jimfb не следует точно, о каком языковом ограничении вы говорите и как оно связано с упрощением API в целом. Не могли бы вы объяснить более подробно или на примере? Мне нужно, чтобы это объяснение было немного упрощено / расширено, чтобы понять ...

Я думаю, он имеет в виду, что нет встроенного быстрого способа проверить, есть ли this.props === nextProps потому что они могут быть или не быть двумя отдельными объектами.

{ a: 1 } === { a: 1 } возвращает false, потому что это два отдельных объекта, но

var a = { a: 1 };
var b = a;
a === b

дает истину, потому что на самом деле они оба являются ссылкой на один и тот же объект.

Мы могли бы рекурсивно проверять каждое свойство на равенство, но это могло быть довольно медленно. Вот почему мы должны реализовать shouldComponentUpdate

@kmalakoff Я не хочу отвлекаться от этой https://gist.github.com/jimfb/9ef9b46741efbb949744

TL; DR: @ akinnee-gl совершенно прав в своем объяснении (спасибо!). С незначительной поправкой, что мы не всегда могли выполнять рекурсивную проверку в любом случае (даже если бы мы хотели, и производительность не была проблемой), потому что нет способа рекурсивно проверить функцию обратного вызова, переданную в качестве опоры.

Тем не менее, давайте сохраним эту тему по теме: P.

Спасибо вам обоим! Хммм, до сих пор не совсем понятно, почему упрощение API не является вариантом решения этой проблемы. Добавлю комментарии по сути ...

Если кто-то хочет участвовать в более широком спектре решений, присоединяйтесь к нам!

@jimfb @calmdev Я попробовал ваши предложения и теперь полностью понимаю, почему componentDidReceiveProps самом деле не добавляет ничего нового и не должен добавляться в качестве дополнительной функции. Раньше я доверял вашему слову, но теперь мне стало интуитивно понятно, почему это так.

Чтобы проиллюстрировать, как я пришел к этому осознанию, я хотел бы поделиться своим новейшим кодом. Возможно, это поможет и другим.

Взгляните на компонент страницы моего профиля:

class ProfilePage extends Component {
  static propTypes = {
    fetchCurrentProfile: PropTypes.func.isRequired,
    currentProfile: PropTypes.object.isRequired
  }

  constructor(props) {
    super(props)
    this.state = { currentProfile: {} }
  }

  componentHasNewRouteParams(routeParams) {
    this.props.fetchCurrentProfile(routeParams.id)
  }

  componentWillMount() {
    this.componentHasNewRouteParams(this.props.routeParams)
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.routeParams.id !== this.props.routeParams.id) {
      this.componentHasNewRouteParams(nextProps.routeParams)
    }
    this.setState({
      currentProfile: nextProps.currentProfile
    })
  }

  render() {
    var profile = this.state.currentProfile
    return (
      <div>
        <h1>{ profile.name ? profile.name : null }</h1>
      </div>
    )
  }
}

Когда компонент смонтирован, у него нет данных профиля, и ему необходимо загрузить профиль из внешнего источника.

Итак ... Я вызываю функцию fetchCurrentProfile (заключенную в componentHasNewIdProp ), которая извлекает данные из внешнего источника, а затем отправляет действие обновления в Redux. Редуктор получает это действие и обновляет состояние приложения, которое затем обновляет реквизиты для компонента ProfilePage .

Теперь, когда свойства были обновлены, вызывается функция componentWillReceiveProps . Но мы не знаем контекста события изменения свойства, поэтому нам нужно выяснить, какие свойства (если таковые имеются) были изменены. Мы видим, что идентификатор такой же, поэтому нам не нужно снова вызывать fetchCurrentProfile . Затем мы обновляем state.currentProfile чтобы компонент знал о необходимости повторного рендеринга с новыми данными профиля (да, я мог бы сделать это с помощью реквизита, но есть еще одна причина, о которой я не хочу здесь вдаваться). В это время мы могли бы проверить, изменилось ли значение props.currentProfile перед обновлением состояния, но это не дорогостоящая операция, поэтому не имеет значения, проверяем ли мы.

Затем ... допустим, мы хотим перейти от одного профиля к другому. Щелкаем ссылку, и роутер инициирует смену маршрута. Теперь у нас есть новые параметры маршрута, и запускается функция componentWillReceiveProps . Мы видим, что id изменилось, и поэтому мы снова вызываем fetchCurrentProfile (через componentHasNewIdProp ).

Когда новая выборка возвращается и отправляет действие redux, опора currentProfile снова обновляется, затем обновляется состояние, и компонент повторно визуализируется с новым представлением.

В этом примере показано, как должно быть различное поведение при монтировании компонента и при получении компонентом новых свойств. Он также демонстрирует, как свойства должны быть проверены на наличие изменений в последнем случае и как определенные функции должны вызываться только при определенных изменениях свойств.

Оглядываясь назад, я понял, что по сути то, что мне нужно, это функция, которая обновлялась бы только при изменении routeParams . Однако нет необходимости добавлять еще одну функцию, потому что мы можем делать все, что захотим, с существующими функциями, плюс мы не хотим усложнять жизненный цикл.

Что-то, что может здесь помочь, - это дополнительная документация по жизненному циклу, рабочие примеры и т. Д.

Спасибо за помощь ребята. Пожалуйста, дайте мне знать, если я здесь что-то не так. Я надеюсь, что это будет полезно для всех, кто наткнется на эту ветку в будущем.

@ shea256 ваш пример кажется разумным случаем для того, чтобы не нуждаться в новом API из-за возможности достичь того, что вам нужно, без новой точки зрения API.

Я изложил другие мысли в: https://gist.github.com/jimfb/9ef9b46741efbb949744 , но я все еще чувствую, что мы должны быть в состоянии упростить API, чтобы писать меньше кода ...

Представьте, что api позволил вам написать ту же функциональность, но с меньшим количеством шаблонов:

class ProfilePage extends Component {
  static propTypes = {
    fetchCurrentProfile: PropTypes.func.isRequired,
    currentProfile: PropTypes.object.isRequired
  }
  state = {currentProfile: {}}; // not material to example (babel-stage-1)

  // called both on init and update
  willReceiveProps(nextProps) {
    if (!this.props || (this.props.routeParams.id !== nextProps.routeParams.id)
      nextProps.fetchCurrentProfile(nextProps.routeParams.id)
  }

  render() {
    var profile = this.props.currentProfile;
    return (
      <div>
        <h1>{ profile.name ? profile.name : null }</h1>
      </div>
    )
  }
}

Идея, которая, я надеюсь, будет рассмотрена, заключается в уменьшении площади поверхности API, чтобы сократить количество путей кода и шаблонов. Я интересно , если обсуждение на якорь на первоначальном запросе , чтобы добавить что - то , когда , может быть , глядя на него с другой точки зрения было бы открыть новые пути.

Это всего лишь один вариант (я предпочитаю receiveProps (prevProps), потому что в идеале был бы только один метод, связанный с prop, где вы могли бы использовать this.props), но важная часть заключается в том, что существует один путь кода для инициализации компонент и любые изменения свойств, а не различные пути кода, которые у нас есть сейчас с разными сигнатурами API. Какое бы ни было решение, люди соглашаются стать лучше.

Я надеюсь, что эта проблема может решить эту проблему (чтобы прояснить мою потребность / проблему!), И я хотел бы провести более широкий мозговой штурм о том, как может выглядеть это решение (подумайте о том, чтобы убрать API, изменить методы жизненного цикла, сокращение имен методов теперь, когда ES6 широко доступен, а компоненты, кажется, являются шагом вперед и т. д.).

@ akinnee-gl, иметь приложение, состоящее из множества крошечных компонентов, намного лучше, чем из нескольких тяжелых компонентов, по нескольким причинам:

  1. Такой подход связан с «принципом единой ответственности».
  2. Вы можете четко видеть, какой компонент что делает
  3. Вы можете легко составлять элементы и расширять компоненты, обернув
  4. Методы и свойства не противоречат друг другу
  5. Легче расширить функцию, потому что один компонент инкапсулирует одну функцию

Что касается вашего уведомления о том, что чтение из магазина при каждом рендеринге может быть неэффективным, я привел суть, которая показывает основную идею о том, как реализовать компонент промежуточного программного обеспечения для кеширования: https://gist.github.com/me-andre/0ca8b80239cd4624e6fc

Я предпринял еще один шаг к принципам / целям, которые могут привести к улучшенному API (надеюсь, основанному на полученном методе реквизита, как предлагается в этом выпуске):

(A) упростить структуру кода / уменьшить шаблон
(B) храните реквизиты (входы) отдельно от состояния (внутренние)
(C) разрешить метод управления рендерингом компонента
(D) упростить именование методов на основе предположения о подклассе компонентов
(E) уменьшить количество методов props до одного как для инициализации, так и для изменений.
(F) метод props должен иметь this.props с последним значением

По некоторым из них есть пояснения по существу.

@kmalakoff По

@kmalakoff Многие из упомянутых вами моментов относятся к другим темам. Например, (D) отвечает https://github.com/reactjs/react-future/issues/40#issuecomment -142442124 и https://github.com/reactjs/react-future/issues/40#issuecomment - 153440651. Это подходящие темы для обсуждения. Если вы ищете более общий / целостный дизайн / обсуждение API, то, вероятно, подходящим средством будет

@jimfb Я подумал об этом после того, как вы предложили мне переместить это в суть и на самом деле не верю, что эта строка анализа не по теме ... причина, по которой меня интересует эта проблема с полученными реквизитами, заключается в том, что я считаю, что Полученный метод жизненного цикла реквизита позволит мне уменьшить шаблон.

Пожалуйста, послушайте меня ... сокращение шаблона - это проблема, которую метод жизненного цикла в этой проблеме мог бы решить для меня.

Я считаю, что это также может быть важной частью причины, по которой люди заинтересованы в этом методе жизненного цикла, потому что текущий API поддерживает шаблон больше, чем нам хотелось бы.

Тем не менее, я был бы счастлив попытаться отделить улучшения API и, например, сосредоточиться только на изучении пространства решений для улучшения API, связанного с реквизитами в этой проблеме.

@kmalakoff Тема обсуждения определяется заголовком и первым сообщением. Заголовок - componentDidReceiveProps Please не lots of general ways to reduce boilerplate . Эта тема касается сокращения шаблонов, в частности, за счет введения очень специфического метода жизненного цикла. Само по себе это обсуждение уже достаточно детализировано, без введения дополнительных тем (например, переименования всех функций), которые уже обсуждаются в других вопросах.

Ваши замечания ценны, и я призываю вас провести обсуждение, просто переместите его в другой поток, потому что мы хотим, чтобы потоки github были сосредоточены на рассматриваемой теме. В противном случае дискуссии будет слишком сложно отслеживать / управлять. Темы существуют по большинству поднятых вами тем.

@jimfb, почему люди просят componentDidReceiveProps ? Причина, по которой меня это интересует, состоит в том, чтобы уменьшить количество шаблонов, относящихся к реквизитам . Я не уверен, как изложить это более четко, чтобы меня можно было услышать и понять.

Вы спросили о проблемах и вариантах использования, и я назвал это проблемой для меня и показал вышеупомянутый вариант использования (ответ на @ shea256), который кажется по теме, в рамках и, с моей точки зрения, важен для решения Эта проблема.

Я согласился воздержаться от расширения охвата до общих проблем / улучшений API и буду. Я обещаю! :подмигивание:

К вашему сведению: как я уже говорил ранее, причина, по которой я начал мысленный эксперимент вокруг этой проблемы, заключалась в том, что аргументы казались слишком узко сфокусированными (например, мы уже можем делать все, что нам нужно с текущим API), а скорее определяли основные мотивы, стоящие за проблема. Как вы знаете, иногда вам нужно сделать шаг назад, затем сосредоточиться, затем взглянуть под другим углом, затем пересмотреть предположения, исследовать основные причины запроса и т. Д., Чтобы найти лучшее решение проблемы. Надеюсь, я помог привлечь к этому внимание, и мое участие поможет решить эту проблему к нашему удовлетворению! (конечно, я также надеюсь, что React API будет развиваться, чтобы быть более удобным в использовании, и продолжу это обсуждение в другом месте ... спасибо за ссылки)

@kmalakoff , хорошо, чтобы уменьшить шаблон, вы просто создаете повторно используемые классы или компоненты, из которых вы создаете подклассы или составляете другие компоненты. Никто не мешает вам определить общий метод для всех компонентов в вашем приложении. React действительно обеспечивает отличную расширяемость: вы можете определять компоненты как объекты, функции или классы. Вы можете расширять компоненты путем создания подклассов, миксинов или фабрик.

@ me-andre - это вариант, если эта проблема не приводит к улучшению API.

Этот вопрос был поднят, чтобы способствовать обсуждению желания внести изменения в API, чтобы улучшить его. Следует рассмотреть возможность окончательного изучения альтернатив в клиентском коде, но если есть веские аргументы в пользу изменения API, такого рода обходные пути не понадобятся. Чтобы противостоять аргументам, также необходимо указать, каким должен быть улучшенный API, а затем сравнить его с текущим API, чтобы оценить их.

Например, если вы должны продемонстрировать, что текущий API без определения настраиваемого суперкласса может использоваться для сокращения шаблонного кода (например, мы неправильно используем API или в текущем API есть функции, которые можно использовать для достижения аналогичный уровень сокращения шаблонов и т. д.) или докажите, почему улучшения невозможны (например, общих решений не существует, потому что существует основной вариант использования, который не может поддерживаться в наших вариантах улучшения API) и т. д., вы могли бы сделать случай, когда API уже достаточно хорош.

Если API требует, чтобы люди писали собственные суперклассы для базовых и общих шаблонов потока управления, это усиливает аргумент в пользу того, что API нужно улучшать.

Любопытно - в чем причина того, что перед установкой не запускается componentWillReceiveProps ? Если задуматься, компонент действительно получает реквизиты при инициализации. Он просто не получает нового реквизита.

Каковы варианты использования, в которых вы хотели бы запускать что-то только при получении новых реквизитов (а не при первоначальном получении реквизитов)?

Я мог упустить что-то очевидное здесь, я просто пытался согласовать точки зрения различных сторон.

В любом случае, если бы componentWillReceiveNewProps были важны в некоторых случаях, можно было бы смоделировать это с помощью модифицированного componentWillReceiveProps , выполнив проверку входящего реквизита.

@kmalakoff, если componentWillReceiveProps запускается в первый раз, будет ли это соответствовать вашим стандартам упрощения API?

componentWillReceiveProps не запускается при монтировании - это не причина, по которой люди просят componentDidReceiveProps . Люди просят componentDidReceiveProps потому что они написали все свои методы для доступа к this.props . Когда вызывается componentWillReceiveProps , передаются следующие свойства, но this.props еще не изменилось, что означает, что мы должны передать nextProps во все методы, которые вызываются в ответ на componentWillReceiveProps , вместо того, чтобы оставить их как есть. В итоге мы получаем тонну props = props || this.props и тонну передачи props в каждую функцию, которую мы вызываем.

@ shea256 положительные моменты. Наличие разных путей кода для инициализации и изменений - одна из основных причин использования шаблона prop. Другая основная причина заключается в использовании разных сигнатур для обработки реквизита, как указывает @ akinnee-gl.

Вот почему я пытаюсь расширить рассматриваемое пространство решений (например, также вызвать init), поскольку на самом деле может быть даже лучшее решение для большего сокращения шаблонов prop.

Надеюсь, мы сможем продвинуться дальше:

Перед новым крючком

componentWillMount() {
  this.setup(this.props.id);
}

componentWillReceiveProps(next) {
  this.setup(next.id);
}

setup(id) {
  UserActions.load(id);
}

После нового крючка (пересмотренный)

componentDidReceiveProps(prevProps) {
  UserActions.load(this.props.id);
}

или если UserActions.load не может проверить текущий загруженный идентификатор:

componentDidReceiveProps(prevProps) {
  if (!prevProps || (prevProps.id !== this.props.id))
    UserActions.load(this.props.id);
}

@kmalakoff , я говорю о том, что улучшение API абсолютно доступно для вас прямо сейчас: вы можете создать свою собственную фабрику компонентов, а затем поделиться с нами (вместе с примерами использования). Это сделает ваше предложение и логику в сотни раз понятнее. Поскольку все точки жизненного цикла уже имеют соответствующие обратные вызовы, вы можете ввести любой новый метод в любой точке жизненного цикла / состоянии компонента. Вы даже можете смешать эмиттер событий с вашим компонентом и сделать возможным присоединение нескольких обработчиков для изменения состояния.

@ shea256 , одна из возможных причин, по которой componentWillReceiveProps не запускается перед первым рендерингом, заключается в том, что в то время не существует такой вещи, как this.props . В componentWillReceiveProps вы обычно сравниваете this.props[propName] с newProps[propName] . Если метод будет запущен до 1-го рендеринга, вам также придется проверять наличие this.props . Более того, весь компонент полностью не инициализирован в то время, когда он получает props перед начальным рендерингом, у него даже нет state .

@kmalakoff , я дважды setup или подобных хаков. Не могли бы вы рассказать, почему вы все еще стремитесь изменить поведение компонента React вместо того, чтобы корректировать свой код так, чтобы он интегрировался с React? Было бы здорово, если бы вы просто указали, где мои образцы не подходят для вашего варианта использования.

@ akinnee-gl, причина не вводить новый метод для простого доступа к this.props при обновлении заключается в том, что у нас есть такой метод - он называется render() . Он даже вызывается после проверки на shouldComponentUpdate() - обычно это место, где вы делаете this.props !== newProps или _.isEqual(this.props, newProps) и т. Д.
Если вы чувствуете, что у вас должен быть отдельный метод для некоторой настройки, почему бы просто не создать подкласс компонента React и не определить следующий метод render()

this.setup(this.props);
return this._render();

Я просто не понимаю, как это упрощает экосистему React, но вы продолжаете просить об этом.

@ me-andre предпосылка этой проблемы заключается не в том, что мы не можем достичь того, чего хотим с текущим API, или что мы не можем обойти текущий API в клиентском коде. Предпосылка этой проблемы заключается в том, что текущий API React не оптимален и нуждается в улучшении ; например, если бы вы придумали принципы того, как будет выглядеть оптимальный API (как я пытался это сделать выше), текущий API React будет оценен в среднем диапазоне, потому что он неоптимален / недостаточен в ряде областей.

К сожалению, предоставление в клиентском коде способов обхода React API не устраняет коренных причин проблемы, уводит дискуссию от решения основных проблем и не ведет к дебатам о потенциальных решениях для улучшения React API.

Короче говоря, у меня уже есть обходные пути, которые работают для меня, потому что у меня есть куча приложений React в производстве, поэтому лучшие практики могут быть полезны для публикации в блоге, но использование их в качестве линии обсуждения в этом вопросе просто отвлечет нас от настоящие дебаты об истинной цели этой проблемы: как улучшить React API (в этом выпуске ограничиваются вариантами использования и шаблоном).

React 1.0 должен быть нацелен на вершину, а не на середину. Увеличение основной версии может нарушить обратную совместимость, поэтому давайте выберем наилучший API, основанный на том, что мы узнали за все эти годы использования версии 0.x.

(К вашему сведению: я думаю, что люди могут не интересоваться вашими примерами так, как вы надеетесь, потому что они приходят сюда не для того, чтобы узнать о текущем API, а потому, что они ищут / надеются на улучшения API, например, они может быть воспринято как благие намерения, но слегка несогласованное)

Я думаю, что люди могут не интересоваться вашими примерами так, как вы надеетесь, потому что они приходят сюда не для того, чтобы узнать о текущем API, а потому, что они ищут / надеются на улучшения API.

Хорошо, вы пришли с предложением по улучшению API, но затем вы показываете код, который слишком далек от "лучших практик React". Некоторые вещи очень похожи на наихудшие практики. Затем вы говорите: «Это причина перемены». Да, это причина изменения, но не в кодовой базе React.
Я покажу вам, как реорганизовать код, чтобы он хорошо работал с React, но просто игнорируйте демонстрацию правильного использования и продолжайте настаивать. Это не похоже на конструктивные дебаты.

К сожалению, предоставление в клиентском коде способов обхода React API не устраняет первопричины проблемы.

Когда вы оборачиваете низкоуровневый API-интерфейс более высокоуровневым, это не обходной путь, а обычная практика. Многие блестящие фреймворки следуют этой идее.
Я все время говорю, что оберните существующий уродливый API тем, чем вы хотели бы поделиться, и поделиться с нами. Наряду с примерами использования и объяснениями, почему это стало лучше, это было бы невероятно.
Опять же, я просто не вижу причины, почему бы вам просто не сделать это. Потому что, если то, о чем вы говорите, является общей проблемой, это будет большим подспорьем для многих людей.

У меня есть куча приложений React в производстве, поэтому лучшие практики могут быть полезны для публикации в блоге, но использование их в качестве линии обсуждения в этом выпуске просто отвлечет нас от настоящих дебатов.

Что вы обычно делаете при проектировании / проектировании API, так это прогнозирование проблем, гипотезы о вариантах использования и т. Д. Причина, по которой люди строят гипотезы, не в том, что они пытаются абстрагироваться от реального мира в поисках идеала. Просто нехватка опыта - нельзя получить опыт «заранее».

Вы говорите, что у вас есть такой опыт, вы видели реальные проблемы и у вас есть обходные пути, которыми вы можете поделиться. Именно это может сделать эту дискуссию «реальной» и «эффективной». Сам React был построен на реальных проблемах и задачах - поэтому он решает реальные проблемы архитектуры, а не просто «как написать привет мир в 3 строки».

@ me-andre Я слышу вас, и ваша аргументация ясна.

Вы правы в том, что центральное место в моем аргументе заключается в том, что если мы установим лучшие принципы, основанные на нашем коллективном опыте использования текущего API React, и откроем дискуссию, чтобы включить недогматические решения, которые могут изменить API некоторыми фундаментальными способами, мы можем и должны воспользуйтесь возможностью улучшить React API, чтобы он стал еще лучше, чем сегодня. Не будем останавливаться на достигнутом, когда есть куда совершенствоваться!

Как бы вы оценили текущий API React в отношении свойств? (скажем, используя буквенные оценки: от F до A +), почему и что бы вы сделали, чтобы улучшить его, если он ниже A +?

@ me-andre у вас была возможность подготовить свой рейтинг и анализ? Мне интересно услышать, что, по вашему мнению, является сильными, слабыми сторонами и возможностями текущего API.

+1

+1 Пожалуйста

Есть ли обходной путь? Мне нужен ComponentDidReceiveProps

Я сделал эту проблему более года назад и с тех пор использую React ежедневно. Я больше не думаю, что есть вариант использования componentDidReceiveProps, который оправдывает увеличение API.

@AlexCppns, что вы пытаетесь сделать?

@iammerrick , на самом деле все в порядке, я просто неправильно понял, как используется componentWillReceiveProps.

Связанное обсуждение: https://github.com/facebook/react/issues/7678

Я сталкивался с этим несколько раз, и в итоге сделал следующее:

componentWillReceiveProps(nextProps) {
  if (this.props.foo !== nextProps.foo) this.needsUpdate = true;
}
componentDidUpdate() {
  if (this.needsUpdate) {
    this.needsUpdate = false;
    // update the dom
  }
}

Это не так уж и плохо.

@brigand , флаг не нужен - вы можете сравнивать реквизиты в пределах componentDidUpdate() :

componentDidUpdate(prevProps) {
    let needsUpdate = prevProps.foo !== this.props.foo;
    // ...whatever
}

Кроме того, ваше решение может быть легко сломано shouldComponentUpdate() , что может помешать повторному рендерингу.

Ой, здорово, спасибо!

@jimfb Я думаю, что у меня есть пример синхронного использования ниже. Я думаю, что componetDidReceiveProps было бы идеально для этого.

  componentDidMount() {
    this._selectAll()
  }

  componentDidUpdate(prevProps) {
    let shouldUpdateSelected = (prevProps.recordTypeFilter !== this.props.recordTypeFilter) ||
      (prevProps.statusFilter !== this.props.statusFilter) ||
      (prevProps.list !== this.props.list)

    if (shouldUpdateSelected) { this._selectAll() }
  }

  _selectAll() {
    this.setState({ selectedIds: this._getFilteredOrders().map((order) => ( order.id )) })
  }

  _getFilteredOrders() {
    let filteredOrders = this.props.list

    // recordTypeFilter
    let recordTypeFilter = this.props.recordTypeFilter
    filteredOrders = _.filter(filteredOrders, (order) => {
        // somelogic
    })

    // statusFilter
    let statusFilter = this.props.statusFilter
    filteredOrders = _.filter(filteredOrders, (order) => {
        // somelogic
    })

    return filteredOrders
  }

@chanakasan , в вашем примере отсутствует метод render() который важен как для понимания вашего примера, так и для предложения лучшего решения.
Во-вторых, ваш код связан с некоторой настраиваемой бизнес-логикой и его нелегко читать. Не стесняйтесь объяснять, а не просто вставляйте копипаст в других.
Однако я прочитал ваш пример и хотел бы поделиться следующими мыслями:

  1. Это вариант использования componentWillReceiveProps , а не componentDidUpdate . Если вы переключитесь на componentWillReceiveProps , ваш компонент будет повторно отображать в два раза меньше, сохраняя при этом ту же логику. Спустя год после того, как я покинул это обсуждение, я все еще не вижу никаких вариантов использования для componentDidUpdate за исключением реакции на изменения DOM.
  2. Однако гораздо лучше было бы вообще избегать перехватов и перенести всю логику на метод render() / перейти на компонент без состояния, потому что this.state.selectedIds самом деле не является состоянием. Это чисто вычислено из реквизита.

Привет @ me-andre! Спасибо, что нашли время ответить. Я прочитал обсуждение на этой странице и хочу поблагодарить вас за участие в нем.

Да, componentDidReceiveProps не нужен, но без него все кажется загадочным.
Вы сказали your component would re-render twice as less если я использую componentWillReceiveProps . Для меня это все еще загадка.

Я подумал и попробовал две вещи, которые вы предлагали раньше, но потерпел неудачу.

  1. componentWillReceiveProps не будет работать, потому что функция _getFilteredOrders() вызываемая из _selectAll() нуждается в newProps.
  2. Я не мог придумать способ получить selectedIds не сохраняя его в состоянии.

Я только что создал полный пример из своего варианта использования. Я сделал это максимально простым для понимания.

Не могли бы вы взглянуть и указать мне правильное направление. Если я смогу понять это правильно, я поделюсь этим примером в сообщении в блоге, чтобы помочь другим.

@chanakasan , давай, это похоже на производственный код. Я не собираюсь читать все это и помогать вам с вашим проектом бесплатно.

Однако я могу указать вам правильное направление:

  1. И componentWillReceiveProps() и componentDidUpdate() могут получить доступ к предыдущим и следующим реквизитам. Это ясно видно из официальных документов React.
  2. Из полной копии вашего кода теперь очевидно, что вы используете состояние для хранения выбранных идентификаторов, которые пользователь может переключать. Тогда можно использовать состояние, но все же componentWillReceiveProps() вызовет повторную визуализацию в два раза меньше. (поскольку рендеринг в любом случае произойдет после componentWillReceiveProps() , setState() просто обновит состояние для предстоящего рендеринга).
  3. Пожалуйста, взгляните на документацию . Экономьте свое время и уважайте других.

@ me-andre Я думаю, что понимаю вашу точку зрения о componentWillReceiveProps используя эту строку в документации:

componentWillReceiveProps () не вызывается, если вы просто вызываете this.setState ()

Но предостережение при использовании componentWillReceiveProps заключается в том, что мне придется передавать nextProps функциям.

Постараюсь последовать твоему совету. Спасибо, я ценю это.

Кстати, в мои намерения не входило, чтобы вы помогали с моим продюсерским проектом бесплатно :). Это более полный пример из моего предыдущего короткого примера, позволяющий заполнить любые пробелы в моем варианте использования.

что, если мы будем использовать это вместе с shouldComponentUpdate?
где мы не хотим обновлять состояние компонента или перерисовывать,
но нам нужно, чтобы this.props использовал последние реквизиты для выполнения некоторых ручных скриптов после получения реквизита.

Моя работа - установить this.props на новые реквизиты перед запуском желаемой функции

Было бы неплохо иметь для этого ComponentDidMount или какой-нибудь крючок. Почему? Потому что иногда нам нужно выполнять другие вычисления, которые не зависят от жизненного цикла React.

Например: у меня есть родительский компонент, размер которого можно изменять. Дочерний компонент отвечает за визуализацию карты OpenLayer, которая имеет функцию, отвечающую за изменение размера карты. Однако это должно произойти после того, как дочерний элемент получит реквизиты от родителя, а также совершит другие вычисления в жизненном цикле React.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги