Предположим, у вас есть простое приложение для реагирования (см. Мой codeandbox):

import React from 'react';
import { render } from 'react-dom';

class RenderPropComponent extends React.Component {
  render() {
    console.log(this.example.test());
    return this.props.render();
  }
}

class Example extends React.Component {
  test = () => console.log('Test successful!');

  render() {
    return <h1>I am an example!</h1>;
  }
}

const App = () => (
  <RenderPropComponent
    render={() => {
      return (
        <Example ref={node => this.example = node} />
      )
    }}
  />
);

render(<App />, document.getElementById('root'));

Это вызывает ошибку:

TypeError
Cannot read property 'test' of undefined

Как я могу назначить ref компоненту, визуализируемому с помощью рендеринга?

Я знаю, что могу добиться этого с помощью this.props.children следующим образом:

class RenderPropComponent extends React.Component {
  const childComponents = React.Children.map(this.props.children, child => {
      return React.cloneElement(child, {ref: node => this.example = node})
  });
  console.log(this.example.test());
  render() {  
    return <div>{childComponents}</div>;
  }
}

...

<RenderPropComponent>
    <Example />
</RenderPropComponent>

Но я бы хотел иметь возможность использовать для этого рендер-опору! Какие-либо предложения?

4
andrewhl 2 Янв 2018 в 00:51

2 ответа

Лучший ответ

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

import React from 'react';
import { render } from 'react-dom';
const styles = {
  fontFamily: 'sans-serif',
  textAlign: 'center',
};

class Hello extends React.Component {
  constructor(props) {
    super(props);
    this.setRef = this.setRef.bind(this);
  }
  setRef(node) {
    this.example = node; 
  }
  render() {
    console.log(this.example && this.example.test());
    return this.props.render(this.setRef);
  }
}

class Example extends React.Component {
  test = () => console.log('Test successful!');

  render() {
    return <h1>I am an example!</h1>;
  }
}

const App = () => (
  <div style={styles}>
    <Hello 
      name="CodeSandbox" 
      render={(setRef) => {
        return (
          <Example ref={setRef} />
        )
      }}
    />
    <h2>Start editing to see some magic happen {'\u2728'}</h2>
  </div>
);

render(<App />, document.getElementById('root'));

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

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

7
Gleb Kost 1 Янв 2018 в 22:05

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

const App = () => (
  <RenderPropComponent
    render={() => {
      return (
        <Example ref={node => this.example = node} />
      )
    }}
  />
);

Если вы еще не заметили этого, взгляните на этот фрагмент:

class Foo {
  constructor(stuffToDo) {
    this.bar = ‘bar’;
    this.stuffToDo = stuffToDo
  }
  doStuff() {
    this.stuffToDo();
  }
}

new Foo(() => console.log(this.bar)).doStuff();

Это будет регистрировать undefined, а не bar, потому что this является производным от текущего закрытия.

-1
Kit Isaev 1 Янв 2018 в 22:08