useRef
useRef hook là một function trả về một object
với thuộc tính current
được khởi tạo thông qua tham số truyền vào.
Object được trả về này có thể mutate và sẽ tồn tại xuyên suốt vòng đời của component.
Trong flow React điển hình, props là cách duy nhất mà các component cha tương tác với các component con của chúng. Để sửa đổi một component con, bạn re-render nó với các props mới.
Tuy nhiên, có một số trường hợp bạn cần phải sửa đổi bắt buộc một component con bên ngoài flow thông thường.
Component con được sửa đổi có thể là một React component hoặc có thể là một phần tử DOM.
Đối với cả hai trường hợp này, React cung cấp một phương pháp.
const refContainer = useRef(initialValue);
useRef trả về một đối tượng ref của DOM có thể thay đổi qua thuộc tính .current
.
useRef.current
có thể gán trực tiếp được giá trị.
ref là gì?
Trong React, ref là một thuộc tính của một tag hay một element đại diện cho chính nó.
ref cho phép chúng ta truy cập đến DOM node hoặc React element đã được mount.
Trong Vanilla Javascript, chúng ta làm việc với DOM elements bằng cách gọi document.getElementById().
VanillaJS là một cái tên để chỉ việc sử dụng Javascript thông thường mà không dùng bất cứ thư viện phụ trợ nào như jQuery.
Mọi người dùng tên này như một lời đùa cợt để nhắc các lập trình viên khác rằng ngày nay nhiều thứ có thể làm được mà không cần đến các thư viện Javascript phụ trợ.
Với ref trong React chúng ta không cần phải làm vậy. Thuộc tính ref sẽ tham chiếu đến chính xác element cần dùng.
<input type="text" ref={textInput} />
ref nhận vào một biến hoặc một function. Nếu là function thì function này sẽ được chạy khi element được mount.
<button ref={(element) => console.log(element)}>Send</button>
Có 2 lý do chính mà chúng ta sẽ sử dụng useRef
Lý do 1: Truy cập DOM nodes
Việc sửa thông tin trên React chỉ là sửa tại Vitual DOM, khi chỉnh sửa kết thúc, Vtual DOM sẽ tìm kiếm sai khác và phản ánh thay rổi vào DOM thật, và tất nhiên việc này sẽ gây nên re-render.
Không giống với chỉnh sửa trên Vitual DOM, việc chỉnh sửa thông qua ref tác động trực tiếp vào DOM và không gây re-render.
cách để ref 1 value vào DOM trong FC:
import React, { useRef } from "react";
const CustomTextInput = () => { const textInput = useRef(); focusTextInput = () => textInput.current.focus(); return (<> <input type="text" ref={textInput} /> <button onClick={focusTextInput}>Focus the text input</button> </>);
}
Trong ví dụ này khi người dùng click chuột vào button thì input có ref là textInput sẽ nhận focus. Điều này useState không thể làm được.
Trong ví dụ Truy cập DOM nodes, useRef() được sử dụng không có giá trị mặc định, tại vì sau đó ref được gắn vào 1 input.
Truy cập React elements
Để truy cập DOM thông qua ref đã được nhắc đến ở trên, nhưng nếu muốn đặt ref cho 1 FC hoặc 1 Component con thì không dùng được:
const Input = () => <input type="text" style={style} />;
return ( <div> <Input ref={inputRef} /> </div>
);
trong trường hợp này inputRef.current == null
Cách fix
Để fix vấn đề này ta dùng forwardRef như một HOC cho Input Component:
const Input = (props, ref) => <input ref={ref} type="text" style={style} />;
export default React.forwardRef(Input);
Lý do 2: Lưu giữ một biến có thể mutate
Có một số trường hợp useRef() được sử dụng để lưu trữ data chứ không phải gắn trực tiếp vào DOM.
Các data này không được hiển thị lên màn hình mà chỉ sử dụng ngầm dưới hệ thống hoặc truyền qua các màn hình.
import React, {useRef, useState} from 'react';
const MessageInputComponent = () => { const [message, setMessage] = useState(""); const count = useRef(0); const clickHandler = () => { if(count.current === 3){ return alert("Message Limit Reached") } count.current += 1; } return( <div> <input onChange = {(e) => setMessage(e.target.value)} value={message}/> <button onClick={clickHandler}>Send</button> </div> )
}
export default MessageInputComponent;
Trong ví dụ trên [count]
không cần hiển thị trên màn hình mà chỉ lưu dữ liệu ngầm, việc thay đổi [count]
không cần re-render
Giá trị [count]
thay đổi thông qua gán trực tiếp giá trị vào [count.current]