无法将ref指定给渲染道具中的组件

时间:2018-01-01 21:51:18

标签: javascript reactjs

假设您有一个简单的反应应用程序(请参阅我的codesandbox):

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>

但我希望能够使用渲染道具来做到这一点!有什么建议吗?

2 个答案:

答案 0 :(得分:7)

不完全确定它是否适合您的情况,但也许您可以将setRef函数作为参数传递给渲染道具?就像在这个分叉的sandbox中一样。

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将不可用(这就是为什么我在控制台日志中添加了一个防护)并且在设置之后,将不会触发重新渲染(因为它是在实例上而不是在状态中设置的)。如果需要重新渲染,我们可以将ref存储在组件状态或强制重新渲染。

另一方面,如果稍后需要在某个事件处理程序中使用ref,那么应该不用重新渲染就可以了。

答案 1 :(得分:-1)

仔细查看,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来自当前的闭包。