Реагируйте: какой правильный способ передать ref на опору?

Я пытаюсь передать ссылку компонента на другой компонент. Поскольку string refs устарели, я использую refback обратного вызова.

Поэтому у меня есть что-то похожее на это:

<One ref={c => this.one = c}/>
<Two one={this.one}/>

Проблема в том, что всякий раз, когда я пытаюсь получить доступ к this.props.one внутри Two я получаю undefined.

Я даже попробовал это на Two:

componentDidMount(){
    setTimeout(()=>{
        console.log(this.props.one);
    },5000)
}

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

Итак, каков правильный способ передачи ссылки на другой компонент?

редактировать

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

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

Предположим, вы хотите создать общий компонент <Form> который инкапсулирует логику отправки в ваш магазин, проверку ошибок и т.д. И вы делаете что-то вроде этого:

<Form>
    <Input/>
    <Input/>
    <Input/>
    <Input/>
    <SubmitButton/> 
</Form>

В этом примере <Form> не может обращаться к экземплярам (и методам) дочерних элементов, поскольку this.props.children не возвращает эти экземпляры. Он возвращает некоторый список псевдо-компонентов.

Итак, как вы можете проверить, не обнаружил ли какой-либо <Input/> ошибку проверки, не передавая ссылку?

Вы должны инкапсулировать эти компоненты в другом компоненте с помощью логики проверки. Например, в <UserForm>. Но так как каждая форма отличается, одна и та же логика должна быть скопирована в <CategoryForm>, <GoupForm> и т.д. Это ужасно неэффективно, поэтому я хочу инкапсулировать логику проверки в <Form> и передать ссылки <Input> компонентов в <Form>.

Ответ 1

В общем, функция "ref" - это анти-шаблон в React. Он существует, чтобы обеспечить развитие, основанное на побочных эффектах, однако для того, чтобы извлечь максимальную пользу из метода программирования React, вы должны стараться избегать "refs", если это возможно.

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

<One ref={c => !this.state.one && this.setState({ one: c })}/>
<Two one={this.state.one}/>

Примечание: без этого !this.state.one это вызовет бесконечный цикл.

Вот пример этого рабочего процесса (посмотрите на консоль, чтобы увидеть, что ребята записаны в соборе): http://codepen.io/anon/pen/pbqvRA

Ответ 2

Теперь это намного проще, используя новый ref api (доступен с React 16 - спасибо perilandmishap за указание на это).

class MyComponent extends React.Component {
  constructor (props) {
    super(props);
    this.oneRef = React.createRef();
  }

  render () {
    return (
      <React.Fragment>
        <One ref={this.oneRef} />
        <Two one={this.oneRef} />
      </React.Fragment>
    }
  }
}

Вы бы потребляли опору в Two как:

this.props.one.current

Несколько вещей, на которые следует обратить внимание при таком подходе:

Ссылка будет объектом с current свойством. Это свойство будет null пока элемент/компонент не будет смонтирован. Как только это установлено, это будет экземпляр One. Ссылка на него должна быть безопасной после монтирования <Two/>.

Как только экземпляр <One/> размонтирован, current свойство в ссылке возвращается к null.

Ответ 3

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

<One ref={c => this.one = c}/>
<Two one={() => this.one}/>

и затем ссылаться на него как

this.props.one()

Если он был установлен, когда вы его вызываете, вы получите значение. До этого вы получите undefined (предполагая, что он иначе не был инициализирован).

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

Учитывая все это, я бы порекомендовал перемещать любой код, используя ref, в " One из Two в компонент, который отображает " One и " Two, чтобы избежать всех проблем, связанных как с этой стратегией, так и с ответом в @Carl Sverre.