Làm cách nào để tôi có thể đính kèm vào ref của một thành phần không trạng thái trong React?


79

Tôi đang tìm cách tạo một thành phần không trạng thái input phần tử của thành phần mẹ có thể được xác thực bởi thành phần mẹ.

Trong ví dụ của tôi bên dưới, tôi đang gặp sự cố trong đó đầu vào refkhông bao giờ được gán cho quyền riêng tư của cha mẹ_emailAddress tài sản .

Khi nào handleSubmitđược gọi this._emailAddressundefined. Có điều gì đó tôi đang thiếu, hoặc có cách nào tốt hơn để làm điều này?

interface FormTestState {
    errors: string;
}

class FormTest extends React.Component<void, FormTestState> {
    componentWillMount() {
        this.setState({ errors: '' });
    }

    render(): JSX.Element {
        return (
            <main role='main' className='about_us'>             
                <form onSubmit={this._handleSubmit.bind(this)}>
                    <TextInput 
                        label='email'
                        inputName='txtInput'
                        ariaLabel='email'
                        validation={this.state.errors}
                        ref={r => this._emailAddress = r}
                    />

                    <button type='submit'>submit</button>
                </form>
            </main>
        );
    }

    private _emailAddress: HTMLInputElement;

    private _handleSubmit(event: Event): void {
        event.preventDefault();
        // this._emailAddress is undefined
        if (!Validators.isEmail(this._emailAddress.value)) {
            this.setState({ errors: 'Please enter an email address.' });
        } else {
            this.setState({ errors: 'All Good.' });
        }
    }
}

const TextInput = ({ label, inputName, ariaLabel, validation, ref }: { label: string; inputName: string; ariaLabel: string; validation?: string; ref: (ref: HTMLInputElement) => void }) => (
    <div>
        <label htmlFor='txt_register_first_name'>
            { label }
        </label>

        <input type='text' id={inputName} name={inputName} className='input ' aria-label={ariaLabel} ref={ref} />

        <div className='input_validation'>
            <span>{validation}</span>
        </div>
    </div>
);

Thêm một console.logchức năng ref của bạn để xem nếu nó được gọi là
Nitzan Tomer

Câu trả lời:


63

CHỈNH SỬA: Bây giờ bạn có thể với React Hooks. Hãy xem câu trả lời của Ante Gulin.

Bạn không thể truy cập Phản ứng như phương pháp (như componentDidMount, componentWillReceiveProps, vv) vào các thành phần không quốc tịch, trong đó có refs. Kiểm tra cuộc thảo luận này trên GH để biết toàn bộ cuộc trò chuyện.

Ý tưởng của không trạng thái là không có một cá thể nào được tạo cho nó (trạng thái). Như vậy, bạn không thể đính kèm a ref, vì không có trạng thái nào để đính kèm tham chiếu.

Đặt cược tốt nhất của bạn là chuyển trong một cuộc gọi lại khi thành phần thay đổi và sau đó gán văn bản đó cho trạng thái gốc.

Hoặc, bạn có thể bỏ qua hoàn toàn thành phần không trạng thái và sử dụng thành phần lớp bình thường.

Từ tài liệu ...

Bạn không thể sử dụng thuộc tính ref trên các thành phần chức năng vì chúng không có phiên bản. Tuy nhiên, bạn có thể sử dụng thuộc tính ref bên trong chức năng kết xuất của một thành phần chức năng.

function CustomTextInput(props) {
  // textInput must be declared here so the ref callback can refer to it
  let textInput = null;

  function handleClick() {
    textInput.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={(input) => { textInput = input; }} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );  
}

@jasan bạn có đang gói dữ liệu đầu vào của mình trong một thành phần khác không? nếu vậy, bạn sẽ cần hiển thị phương thức tiêu điểm trên lớp đó. Nếu bạn vẫn gặp sự cố, hãy kiểm tra giá trị của textInput khi bạn đang gọi tiêu điểm.
Brad B

vâng, tôi đang sử dụng Field từ redux-form. Tôi đã tìm ra giải pháp. chúc mừng
jasan

1
Vâng, bạn vẫn có thể truy cập ref bằng cách chuyển giá trị từ ref cho cha, thông qua một hàm prop, tại thành phần không trạng thái ref={input => innerRef(input)} , trên cha, hãy sử dụng prop mà bạn đã chuyển giống như bạn sử dụng ref bình thường; innerRef={input => this.customInputRef = input}
Eyo Okon Eyo

1
câu trả lời này đã lỗi thời vìv16.8.0
Sebastian Scholle

132

Bạn có thể sử dụng useRefhook có sẵn vìv16.7.0-alpha .

CHỈNH SỬA: Bạn được khuyến khích sử dụng Hooks trong sản xuất kể từ16.8.0 phát hành!

Hooks cho phép bạn duy trì trạng thái và xử lý các tác dụng phụ trong các thành phần chức năng.

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

Đọc thêm trong tài liệu API Hooks


3
@Jafarrezaei Có Hooks đang ở giai đoạn alpha nhưng chắc chắn sẽ được thông qua.
Ante Gulin

1
@AnteGulin vâng nó sẽ được thông qua, nhưng đó là ALPHA. Điều đó có nghĩa là nó có thể không ổn định, không có gì đảm bảo rằng API hiện tại sẽ là thứ khiến nó trở thành sản phẩm cuối cùng. Nó tồn tại hoàn toàn để thử nghiệm và phản hồi, KHÔNG sử dụng cho sản xuất.
user2223059

5
Nó đã hết alpha và ngay cả trong phản ứng gốc bây giờ là 7u7
ValdaXD

4

Điều này là muộn nhưng tôi thấy giải pháp này tốt hơn nhiều. Chú ý đến cách nó sử dụng useRef & cách các thuộc tính có sẵn trong thuộc tính hiện tại .

function CustomTextInput(props) {
  // textInput must be declared here so the ref can refer to it
  const textInput = useRef(null);

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}

Để tham khảo thêm, hãy kiểm tra tài liệu phản ứng


2

Giá trị TextInput của bạn không hơn gì một trạng thái của thành phần của bạn. Vì vậy, thay vì tìm nạp giá trị hiện tại bằng một tham chiếu (ý kiến ​​tồi nói chung, theo như tôi biết), bạn có thể tìm nạp trạng thái hiện tại.

Trong một phiên bản rút gọn (không cần nhập):

class Form extends React.Component {
  constructor() {
    this.state = { _emailAddress: '' };

    this.updateEmailAddress = this.updateEmailAddress.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  updateEmailAddress(e) {
    this.setState({ _emailAddress: e.target.value });
  }

  handleSubmit() {
    console.log(this.state._emailAddress);
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          value={this.state._emailAddress}
          onChange={this.updateEmailAddress}
        />
      </form>
    );
  }
}

1
Việc gán cho trạng thái sẽ hơi khó khăn khi các biểu mẫu trở nên dài dòng, đó là lý do tại sao tôi cố gắng giải quyết vấn đề này với ref. Tôi hoàn toàn tin rằng những gì tôi đang cố gắng làm là không thể thực hiện được ở đây, nhưng bạn có phiền giải thích hoặc tham khảo một bài báo giải thích tại sao refnói chung việc sử dụng là một ý tưởng tồi không?
drawwyatt

Có lẽ bạn có thể soạn thành phần của mình theo nhiều cấp độ? Hãy thử câu hỏi này: stackoverflow.com/questions/29503213/…

1
Có một lời cảnh báo ở đây: facebook.github.io/react/docs/…
Brad B

1

Bạn cũng có thể nhận được giới thiệu về các thành phần chức năng với một hệ thống ống nước nhỏ

import React, { useEffect, useRef } from 'react';

// Main functional, complex component
const Canvas = (props) => {
  const canvasRef = useRef(null);

    // Canvas State
  const [canvasState, setCanvasState] = useState({
      stage: null,
      layer: null,
      context: null,
      canvas: null,
      image: null
  });

  useEffect(() => {
    canvasRef.current = canvasState;
    props.getRef(canvasRef);
  }, [canvasState]);


  // Initialize canvas
  useEffect(() => {
    setupCanvas();
  }, []);

  // ... I'm using this for a Konva canvas with external controls ...

  return (<div>...</div>);
}

// Toolbar which can do things to the canvas
const Toolbar = (props) => {
  console.log("Toolbar", props.canvasRef)

  // ...
}

// Parent which collects the ref from Canvas and passes to Toolbar
const CanvasView = (props) => {
  const canvasRef = useRef(null);

  return (
    <Toolbar canvasRef={canvasRef} />
    <Canvas getRef={ ref => canvasRef.current = ref.current } />
}
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.