Làm thế nào để sử dụng React.earchRef trong một thành phần dựa trên lớp?


111

Tôi đang cố gắng sử dụng React.nticRef, nhưng vấp phải cách làm cho nó hoạt động trong một thành phần dựa trên lớp (không phải HOC).

Các ví dụ tài liệu sử dụng các phần tử và thành phần chức năng, thậm chí gói các lớp trong các hàm cho các thành phần bậc cao hơn.

Vì vậy, bắt đầu với một cái gì đó như thế này trong ref.jstệp của họ :

const TextInput = React.forwardRef(
    (props, ref) => (<input type="text" placeholder="Hello World" ref={ref} />)
);

và thay vào đó định nghĩa nó như sau:

class TextInput extends React.Component {
  render() {
    let { props, ref } = React.forwardRef((props, ref) => ({ props, ref }));
    return <input type="text" placeholder="Hello World" ref={ref} />;
  }
}

hoặc là

class TextInput extends React.Component {
  render() { 
    return (
      React.forwardRef((props, ref) => (<input type="text" placeholder="Hello World" ref={ref} />))
    );
  }
}

chỉ hoạt động: /

Ngoài ra, tôi biết tôi biết, ref không phải là cách phản ứng. Tôi đang cố gắng sử dụng thư viện canvas của bên thứ ba và muốn thêm một số công cụ của họ trong các thành phần riêng biệt, vì vậy tôi cần trình xử lý sự kiện, vì vậy tôi cần các phương pháp vòng đời. Nó có thể đi một con đường khác sau này, nhưng tôi muốn thử điều này.

Các tài liệu nói rằng nó có thể!

Chuyển tiếp tham chiếu không giới hạn đối với các thành phần DOM. Bạn cũng có thể chuyển tiếp các tham chiếu đến các cá thể thành phần của lớp.

từ ghi chú trong phần này.

Nhưng sau đó họ tiếp tục sử dụng HOC thay vì chỉ các lớp.

Câu trả lời:


107

Ý tưởng luôn sử dụng cùng một chỗ dựa cho refcó thể đạt được bằng cách xuất lớp ủy quyền với người trợ giúp.

class ElemComponent extends Component {
  render() {
    return (
      <div ref={this.props.innerRef}>
        Div has ref
      </div>
    )
  }
}

export default React.forwardRef((props, ref) => <ElemComponent 
  innerRef={ref} {...props}
/>);

Vì vậy, về cơ bản, chúng ta buộc phải có một chỗ dựa khác để chuyển tiếp ref, nhưng nó có thể được thực hiện dưới hub. Điều quan trọng là công chúng sử dụng nó như một giới thiệu bình thường.


4
Bạn sẽ làm gì nếu bạn đặt thành phần của mình trong một HOC như React-Redux's connecthoặc marterial-ui's withStyles?
J. Hesters

Vấn đề với connecthoặc là withStylesgì? Bạn nên bọc tất cả các HOC bằng forwardRefvà sử dụng nội bộ "an toàn" để chuyển một tham chiếu đến thành phần thấp nhất trong chuỗi.
Mr Br

Bất kỳ thông tin chi tiết nào về cách kiểm tra điều này trong Enzyme khi bạn đang sử dụng setState?
zero_cool

Xin lỗi, tôi cần thêm một chút chi tiết. Không chắc chắn vấn đề chính xác là gì.
Mr Br

2
Tôi nhận được: Vi phạm bất biến: Các đối tượng không hợp lệ dưới dạng React con (tìm thấy: đối tượng có khóa {$$ typeof, render}). Nếu bạn muốn hiển thị một tập hợp con, hãy sử dụng một mảng để thay thế.
Brian Loughnane

9
class BeautifulInput extends React.Component {
  const { innerRef, ...props } = this.props;
  render() (
    return (
      <div style={{backgroundColor: "blue"}}>
        <input ref={innerRef} {...props} />
      </div>
    )
  )
}

const BeautifulInputForwardingRef = React.forwardRef((props, ref) => (
  <BeautifulInput {...props} innerRef={ref}/>
));

const App = () => (
  <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
)

Bạn cần sử dụng một tên prop khác để chuyển tiếp ref tới một lớp. innerRefđược sử dụng phổ biến trong nhiều thư viện.


trong mã của bạn beautifulInputElementlà nhiều hơn một chức năng chứ không phải là một phần tử Phản ứng, nó phải là như thế nàyconst beautifulInputElement = <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
Olivier Boisse

8

Về cơ bản, đây chỉ là một hàm HOC. Nếu bạn muốn sử dụng nó với lớp học, bạn có thể tự làm điều này và sử dụng các đạo cụ thông thường.

class TextInput extends React.Component {
    render() {
        <input ref={this.props.forwardRef} />
    }
}

const ref = React.createRef();
<TextInput forwardRef={ref} />

Ví dụ như mẫu này được sử dụng ở trong styled-componentsvà nó được gọi là innerRefở đó.


3
Sử dụng một phần mềm hỗ trợ có tên khác như innerReflà hoàn toàn thiếu điểm. Mục tiêu là hoàn toàn minh bạch: Tôi sẽ có thể coi một <FancyButton>như thể nó là một phần tử DOM thông thường, nhưng bằng cách sử dụng phương pháp tiếp cận theo kiểu thành phần, tôi cần nhớ rằng thành phần này sử dụng một phần tử hỗ trợ được đặt tên tùy ý cho các tham chiếu thay vì chỉ ref.
user128216

2
Trong mọi trường hợp, các thành phần được tạo kiểu có ý định bỏ hỗ trợ để cóinnerRef lợi cho việc chuyển ref cho đứa trẻ sử dụng React.forwardRef, đó là điều mà OP đang cố gắng thực hiện.
user128216

5

Điều này có thể được thực hiện với một thành phần bậc cao hơn, nếu bạn thích:

import React, { forwardRef } from 'react'

const withForwardedRef = Comp => {
  const handle = (props, ref) =>
    <Comp {...props} forwardedRef={ref} />

  const name = Comp.displayName || Comp.name
  handle.displayName = `withForwardedRef(${name})`

  return forwardRef(handle)
}

export default withForwardedRef

Và sau đó trong tệp thành phần của bạn:

class Boop extends React.Component {
  render() {
    const { forwardedRef } = this.props

    return (
      <div ref={forwardedRef} />
    )
  }
}

export default withForwardedRef(Boop)

Tôi đã làm việc với trả trước kiểm tra & công bố một gói cho điều này, react-with-forwarded-ref: https://www.npmjs.com/package/react-with-forwarded-ref


3

Trong trường hợp bạn cần sử dụng lại nó trong nhiều thành phần khác nhau, bạn có thể xuất khả năng này thành một cái gì đó như withForwardingRef

const withForwardingRef = <Props extends {[_: string]: any}>(BaseComponent: React.ReactType<Props>) =>
    React.forwardRef((props, ref) => <BaseComponent {...props} forwardedRef={ref} />);

export default withForwardingRef;

sử dụng:

const Comp = ({forwardedRef}) => (
 <input ref={forwardedRef} />
)
const EnhanceComponent = withForwardingRef<Props>(Comp);  // Now Comp has a prop called forwardedRef
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.