Peeps Avatar

Hello, codestus.

Go back

Làm thế nào để chia sẻ internal refs với component khác?

Published at: 03/04/2023

4 mins read

Thuộc tính ref trong React được sử dụng để truy cập một component children hoặc một DOM element. Chúng ta thường sử dụng ref với HOC được cung cấp từ React là forwardRef hoặc useRef hook. Nhưng bạn có bao giờ từ hỏi khi không biết 2 phương thức tương tác với ref ở trên có thể tồn tại trong một thời điểm hay không?.

Khi nhu cầu chúng ta vừa cần internal ref để xử lý các logic bên trong componnet đó, đồng thời cũng muốn chia sẻ internal ref đó cho các component khác sử dụng. Ngay lập tức, bạn sẽ phải quay lại câu hỏi phía trên.

Và câu trả lời là có, chúng ta có thể sử dụng useImperativeHandle hook để giải quyết vấn đề này.

External refs với forwardRef

Chúng ta thường sử dụng forwardRef để thiết kế một component cho phép các components khác tham chiếu đế các yếu tố có ý nghĩa trong một component.

Cho ví dụ như sau:

const TextSubmit = () => (
	<div>
		<input type="text" />
		<button onClick={onClick}>Submit</button>
	</div>
)

Tại component này, chúng ta sẽ quyết định cho phép tham chiếu đến các thuộc tính của phần tử input từ component cha đang chứa nó. Vì vậy, chúng ta sẽ sử dụng forwardRef để chia sẽ các thuộc tính của input cho phần tử cha của nó như sau:

const TextSubmit = forwardRef(({ onClick, ...props }, ref) => {(
	<div>
		<input ref={ref} type="text" />
		<button onClick={onClick}>Submit</button>
	</div>
))

Với forwardRef, thêm ref vào component, chúng ta đã cấp quyền truy cập các thuộc tính internal của input.

function App() {
	const [text, setText] = useState("");
	const inputRef = useRef(null);
	
	useEffect(() => {
		if(inputRef.current) {
			setText(inputRef.current.value);
		}
	}, [inputRef.current])

	return <div>
		<TextSubmit ref={inputRef} />
	</div>
}

export default App;

Ngay lúc này, chúng ta có thể tương tác với chính các thuộc tính của input bên trong <TextSubmit /> component thông qua inputRef. Nếu chúng ta cần ref nội bộ bên trong <TextSubmit /> trước khi forwardRef cho component khác thì sao?.

Internal refs và forwardRef

Ngay lúc này, chúng ta muốn nội bộ <TextSubmit /> component sẽ tự động focus input. Thông thường chúng ta sẽ xử lý như sau.

const TextSubmit = forwardRef(({ onClick, ...props }, ref) => {
	const inputRef = useRef(null);
	
	useEffect(() => {
		if(inputRef.current) {
			inputRef.current.focus();
		}
	}, [inputRef.current])

	return <div>
		<input ref={inputRef} type="text" />
		<button onClick={onClick}>Submit</button>
	</div>
})

Vấn đề xuất hiện ngay

Làm thế nào để chúng ta forward ref của useRef hook cho component cha.?

Cách giải quyết

React đã ra mắt useImperativeHandle, chúng ta có thể forward một internal ref của một component cho component cha của nó theo cách như sau:

const TextSubmit = forwardRef(({ onClick, ...props }, ref) => {
	const inputRef = useRef(null);
	
	useEffect(() => {
		if(inputRef.current) {
			inputRef.current.focus();
		}
	}, [inputRef.current]);
	
	// --> Solution here
	useImperativeHandle(ref, () => inputRef.current);

	return <div>
		<input ref={inputRef} type="text" />
		<button onClick={onClick}>Submit</button>
	</div>
})

Khá đơn giản, bằng việc trả về giá trị trong callback của useImperativeHandle internal ref mà component đó đang sử dụng.

Mình có đưa ví dụ lên codesanbox nếu bạn cần: Codesanbox

Kết luận

useImperativeHandle hook có công dụng khá tốt sau khi được ra mắt. Phía trên là cách chúng ta xử lý một vấn đề về sharing internal refs cho một componnet khác.