Selamat siang teman!
Berikut panduan untuk hook React utama: useState, useEffect, useLayoutEffect, useContext, useReducer, useCallback, useMemo, dan UseRef.
Inspirasi: Lembar contekan React Hooks: Buka solusi untuk masalah umum .
Tujuan dari panduan ini adalah untuk memberikan gambaran singkat tentang tujuan dan kemampuan masing-masing hook. Setelah deskripsi hook, ada kode contoh penggunaannya dan kotak pasir untuk percobaan Anda.
Set lengkap hook tersedia di repositori ini .
- Mendownload repositori
- Instal dependensi: npm i
- Jalankan: npm start
Hooks terletak di direktori "hooks". File utamanya adalah index.js. Untuk menjalankan hook tertentu, Anda perlu menghapus komentar pada import dan render lines yang sesuai.
Tanpa pembukaan lebih lanjut.
useState
useState memungkinkan Anda untuk bekerja dengan status variabel di dalam komponen fungsional.
Status variabel
Untuk menentukan status variabel, panggil useState dengan status awal sebagai argumen: useState (initialValue).
const DeclareState = () => {
const [count] = useState(1);
return <div> - {count}.</div>;
};
Memperbarui status variabel
Untuk memperbarui status variabel, panggil fungsi pembaruan yang dikembalikan oleh useState: const [state, updater] = useState (initialValue).
Kode:
const UpdateState = () => {
const [age, setAge] = useState(19);
const handleClick = () => setAge(age + 1);
return (
<>
<p> {age} .</p>
<button onClick={handleClick}> !</button>
</>
);
};
Bak pasir:
Beberapa status variabel
Dalam satu komponen fungsional, Anda dapat menentukan dan memperbarui status beberapa variabel.
Kode:
const MultStates = () => {
const [age, setAge] = useState(19);
const [num, setNum] = useState(1);
const handleAge = () => setAge(age + 1);
const handleNum = () => setNum(num + 1);
return (
<>
<p> {age} .</p>
<p> {num} .</p>
<button onClick={handleAge}> !</button>
<button onClick={handleNum}> !</button>
</>
);
};
Bak pasir:
Menggunakan objek untuk menentukan status variabel
Selain string dan angka, objek dapat digunakan sebagai nilai awal. Perhatikan bahwa useStateUpdater perlu diteruskan ke seluruh objek karena akan diganti daripada digabungkan dengan yang sebelumnya.
// setState ( ) - useState ( )
// , - {name: "Igor"}
setState({ age: 30 });
//
// {name: "Igor", age: 30} -
useStateUpdater({ age: 30 });
//
// {age: 30} -
Kode:
const StateObject = () => {
const [state, setState] = useState({ age: 19, num: 1 });
const handleClick = (val) =>
setState({
...state,
[val]: state[val] + 1,
});
const { age, num } = state;
return (
<>
<p> {age} .</p>
<p> {num} .</p>
<button onClick={() => handleClick('age')}> !</button>
<button onClick={() => handleClick('num')}> !</button>
</>
);
Bak pasir:
Menginisialisasi status variabel menggunakan fungsi
Nilai awal status variabel dapat ditentukan oleh suatu fungsi.
const StateFun = () => {
const [token] = useState(() => {
const token = localStorage.getItem("token");
return token || "default-token";
});
return <div> - {token}</div>;
};
Fungsi alih-alih setState
Fungsi pembaruan yang dikembalikan oleh useState bisa lebih dari sekadar setState.
const [value, updateValue] = useState(0);
// , ,
updateValue(1);
updateValue((prevVal) => prevVal + 1);
Metode kedua cocok untuk kasus di mana pembaruan bergantung pada status sebelumnya.
Kode:
const CounterState = () => {
const [count, setCount] = useState(0);
return (
<>
<p> {count}.</p>
<button onClick={() => setCount(0)}></button>
<button onClick={() => setCount((prevVal) => prevVal + 1)}>
(+)
</button>
<button onClick={() => setCount((prevVal) => prevVal - 1)}>
(-)
</button>
</>
);
};
Bak pasir:
useEffect
useEffect menerima fungsi yang bertanggung jawab atas efek (samping) tambahan.
Penggunaan dasar
Kode:
const BasicEffect = () => {
const [age, setAge] = useState(19);
const handleClick = () => setAge(age + 1);
useEffect(() => {
document.title = ` ${age} !`;
});
return (
<>
<p> .</p>
<button onClick={handleClick}> !</button>
</>
);
};
Bak pasir:
Menghapus (membatalkan) efek
Praktik yang umum adalah menghilangkan efeknya setelah beberapa saat. Ini bisa dilakukan dengan menggunakan fungsi yang dikembalikan oleh efek yang diteruskan ke useEffect. Di bawah ini adalah contoh dengan addEventListener.
Kode:
const CleanupEffect = () => {
useEffect(() => {
const clicked = () => console.log("!");
window.addEventListener("click", clicked);
return () => {
window.removeEventListener("click", clicked);
};
}, []);
return (
<>
<p> .</p>
</>
);
};
Bak pasir:
Berbagai efek
Beberapa useEffects dapat digunakan dalam komponen fungsional.
Kode:
const MultEffects = () => {
//
useEffect(() => {
const clicked = () => console.log("!");
window.addEventListener("click", clicked);
return () => {
window.removeEventListener("click", clicked);
};
}, []);
//
useEffect(() => {
console.log(" .");
});
return (
<>
<p> .</p>
</>
);
};
Bak pasir:
Perhatikan bahwa panggilan ke useEffect pada rendering ulang bisa dilewati dengan meneruskannya ke array kosong sebagai argumen kedua.
Dependensi efek
Kode:
const EffectDependency = () => {
const [randomInt, setRandomInt] = useState(0);
const [effectLogs, setEffectLogs] = useState([]);
const [count, setCount] = useState(1)
useEffect(() => {
setEffectLogs((prevEffectLogs) => [
...prevEffectLogs,
` ${count}.`,
]);
setCount(count + 1)
}, [randomInt]);
return (
<>
<h3>{randomInt}</h3>
<button onClick={() => setRandomInt(~~(Math.random() * 10))}>
!
</button>
<ul>
{effectLogs.map((effect, i) => (
<li key={i}>{" ".repeat(i) + effect}</li>
))}
</ul>
</>
);
};
Bak pasir:
Dalam kasus ini, kita meneruskan dependensi randomInt ke useEffect sebagai argumen kedua, sehingga fungsinya dipanggil pada render awal, serta setiap kali randomInt berubah.
Lewati efek (ketergantungan array kosong)
Dalam contoh di bawah ini, useEffect meneruskan array kosong sebagai dependensi, jadi efeknya hanya akan bekerja pada rendering awal.
Kode:
const SkipEffect = () => {
const [randomInt, setRandomInt] = useState(0);
const [effectLogs, setEffectLogs] = useState([]);
const [count, setCount] = useState(1);
useEffect(() => {
setEffectLogs((prevEffectLogs) => [
...prevEffectLogs,
` ${count}.`,
]);
setCount(count + 1);
}, []);
return (
<>
<h3>{randomInt}</h3>
<button onClick={() => setRandomInt(~~(Math.random() * 10))}>
!
</button>
<ul>
{effectLogs.map((effect, i) => (
<li key={i}>{" ".repeat(i) + effect}</li>
))}
</ul>
</>
);
};
Bak pasir:
Saat tombol diklik, useEffect tidak dipanggil.
Efek melewatkan (tanpa ketergantungan)
Jika tidak ada larik dependensi, efeknya akan dipicu setiap kali halaman dirender.
useEffect(() => {
console.log(
" ."
);
});
useContext
useContext menghilangkan kebutuhan untuk bergantung pada konsumen konteks. Ini memiliki antarmuka yang lebih sederhana dibandingkan dengan MyContext.Consumer dan rendering props. Di bawah ini adalah perbandingan penggunaan konteks menggunakan useContext dan Context.Consumer.
// Context
const ThemeContext = React.createContext("dark")
//
function Button() {
return (
<ThemeContext.Consumer>
{theme => <button className={thene}> !</button>}
</ThemeContext.Consumer>
}
// useContext
import { useContext } from "react"
function ButtonHook() {
const theme = useContext(ThemeContext)
return <button className={theme}> !</button>
}
Kode:
const ChangeTheme = () => {
const [mode, setMode] = useState("light");
const handleClick = () => {
setMode(mode === "light" ? "dark" : "light");
};
const ThemeContext = React.createContext(mode);
const theme = useContext(ThemeContext);
return (
<div
style={{
background: theme === "light" ? "#eee" : "#222",
color: theme === "light" ? "#222" : "#eee",
display: "grid",
placeItems: "center",
minWidth: "320px",
minHeight: "320px",
borderRadius: "4px",
}}
>
<p> : {theme}.</p>
<button onClick={handleClick}> </button>
</div>
);
};
Bak pasir:
useLayoutEffect
Perilaku useLayoutEffect mirip dengan useEffect, dengan beberapa pengecualian, yang akan kita bicarakan nanti.
useLayoutEffect(() => {
//
}, []);
Penggunaan dasar
Berikut adalah contoh menggunakan useEffect, tetapi dengan useLayoutEffect.
Kode:
const [randomInt, setRandomInt] = useState(0);
const [effectLogs, setEffectLogs] = useState([]);
const [count, setCount] = useState(1);
useLayoutEffect(() => {
setEffectLogs((prevEffectLogs) => [
...prevEffectLogs,
` ${count}.`,
]);
setCount(count + 1);
}, [randomInt]);
return (
<>
<h3>{randomInt}</h3>
<button onClick={() => setRandomInt(~~(Math.random() * 10))}>
!
</button>
<ul>
{effectLogs.map((effect, i) => (
<li key={i}>{" ".repeat(i) + effect}</li>
))}
</ul>
</>
);
};
Bak pasir:
useLayoutEffect dan useEffect
Fungsi yang diteruskan ke useEffect dipanggil setelah halaman dirender, mis. setelah pembentukan tata letak dan rendering elemen. Ini cocok untuk sebagian besar efek tambahan yang seharusnya tidak menghalangi aliran. Namun, jika Anda ingin melakukan manipulasi DOM sebagai efek tambahan, misalnya, useEffect bukanlah pilihan terbaik. Untuk mencegah pengguna melihat perubahan, useLayoutEffect harus digunakan. Fungsi yang diteruskan ke useLayoutEffect dipanggil sebelum halaman dirender.
useReducer
useReducer dapat digunakan sebagai alternatif untuk useState, namun, tujuannya adalah untuk merangkum logika kompleks untuk bekerja dengan status, saat status bergantung pada nilai sebelumnya atau terdapat beberapa status.
Penggunaan dasar
Pada contoh di bawah ini, useReducer digunakan sebagai ganti useState. Panggilan useReducer mengembalikan nilai status dan fungsi pengiriman.
Kode:
const initialState = { width: 30 };
const reducer = (state, action) => {
switch (action) {
case "plus":
return { width: Math.min(state.width + 30, 600) };
case "minus":
return { width: Math.max(state.width - 30, 30) };
default:
throw new Error(" ?");
}
};
const BasicReducer = () => {
const [state, dispath] = useReducer(reducer, initialState);
const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
return (
<>
<div
style={{
margin: "0 auto",
background: color,
height: "100px",
width: state.width,
}}
></div>
<button onClick={() => dispath("plus")}>
.
</button>
<button onClick={() => dispath("minus")}>
.
</button>
</>
);
};
Bak pasir:
Inisialisasi status ditangguhkan ("malas")
useReducer mengambil argumen opsional ketiga, fungsi yang mengembalikan objek status. Fungsi ini dipanggil dengan initialState sebagai argumen kedua.
Kode:
const initializeState = () => ({
width: 90,
});
// , initializeState
const initialState = { width: 0 };
const reducer = (state, action) => {
switch (action) {
case "plus":
return { width: Math.min(state.width + 30, 600) };
case "minus":
return { width: Math.max(state.width - 30, 30) };
default:
throw new Error(" ?");
}
};
const LazyState = () => {
const [state, dispath] = useReducer(reducer, initialState, initializeState);
const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
return (
<>
<div
style={{
margin: "0 auto",
background: color,
height: "100px",
width: state.width,
}}
></div>
<button onClick={() => dispath("plus")}>
.
</button>
<button onClick={() => dispath("minus")}>
.
</button>
</>
);
};
Bak pasir:
Mensimulasikan perilaku this.setState
useReducer menggunakan peredam yang kurang ketat dari Redux. Misalnya, argumen kedua yang diteruskan ke peredam tidak memerlukan properti type. Ini memberi kami peluang menarik.
Kode:
const initialState = { width: 30 };
const reducer = (state, newState) => ({
...state,
width: newState.width,
});
const NewState = () => {
const [state, setState] = useReducer(reducer, initialState);
const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
return (
<>
<div
style={{
margin: "0 auto",
background: color,
height: "100px",
width: state.width,
}}
></div>
<button onClick={() => setState({ width: 300 })}>
.
</button>
<button onClick={() => setState({ width: 30 })}>
.
</button>
</>
);
};
Bak pasir:
useCallback
useCallback mengembalikan callback yang disimpan (dalam cache).
Template pemula
Kode:
const CallbackTemplate = () => {
const [age, setAge] = useState(19);
const handleClick = () => setAge(age < 100 ? age + 1 : age);
const someValue = "some value";
const doSomething = () => someValue;
return (
<>
<Age age={age} handleClick={handleClick} />
<Guide doSomething={doSomething} />
</>
);
};
const Age = ({ age, handleClick }) => {
return (
<div>
<p> {age} .</p>
<p> </p>
<button onClick={handleClick}> !</button>
</div>
);
};
const Guide = React.memo((props) => {
const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
return (
<div style={{ background: color, padding: ".4rem" }}>
<p style={{ color: color, filter: "invert()" }}>
.
</p>
</div>
);
});
Bak pasir:
Dalam contoh di atas, komponen Umur diperbarui dan dirender ulang saat tombol diklik. Komponen Guide juga dirender ulang saat callback baru diteruskan ke props doSomething. Meskipun Guide menggunakan React.memo untuk mengoptimalkan kinerja, itu masih digambar ulang. Bagaimana kita bisa memperbaikinya?
Penggunaan dasar
Kode:
const BasicCallback = () => {
const [age, setAge] = useState(19);
const handleClick = () => setAge(age < 100 ? age + 1 : age);
const someValue = "some value";
const doSomething = useCallback(() => someValue, [someValue]);
return (
<>
<Age age={age} handleClick={handleClick} />
<Guide doSomething={doSomething} />
</>
);
};
const Age = ({ age, handleClick }) => {
return (
<div>
<p> {age} .</p>
<p> </p>
<button onClick={handleClick}> !</button>
</div>
);
};
const Guide = React.memo((props) => {
const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
return (
<div style={{ background: color, padding: ".4rem" }}>
<p style={{ color: color, filter: "invert()" }}>
.
</p>
</div>
);
});
Bak pasir:
UseCallback bawaan
useCallback dapat digunakan sebagai fungsi bawaan.
Kode:
const InlineCallback = () => {
const [age, setAge] = useState(19);
const handleClick = () => setAge(age < 100 ? age + 1 : age);
const someValue = "some value";
return (
<>
<Age age={age} handleClick={handleClick} />
<Guide doSomething={useCallback(() => someValue, [someValue])} />
</>
);
};
const Age = ({ age, handleClick }) => {
return (
<div>
<p> {age} .</p>
<p> </p>
<button onClick={handleClick}> !</button>
</div>
);
};
const Guide = React.memo((props) => {
const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
return (
<div style={{ background: color, padding: ".4rem" }}>
<p style={{ color: color, filter: "invert()" }}>
.
</p>
</div>
);
});
Bak pasir:
useMemo
useMemo mengembalikan nilai tersimpan (dalam cache).
Template pemula
Kode:
const MemoTemplate = () => {
const [age, setAge] = useState(19);
const handleClick = () => setAge(age < 100 ? age + 1 : age);
const someValue = { value: "some value" };
const doSomething = () => someValue;
return (
<>
<Age age={age} handleClick={handleClick} />
<Guide doSomething={doSomething} />
</>
);
};
const Age = ({ age, handleClick }) => {
return (
<div>
<p> {age} .</p>
<p> </p>
<button onClick={handleClick}> !</button>
</div>
);
};
const Guide = React.memo((props) => {
const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
return (
<div style={{ background: color, padding: ".4rem" }}>
<p style={{ color: color, filter: "invert()" }}>
.
</p>
</div>
);
});
Bak pasir:
Template ini identik dengan template useCallback awal, kecuali someValue adalah objek dan bukan string. Komponen Panduan juga dirender ulang meskipun menggunakan React.memo.
Tapi kenapa ini terjadi? Bagaimanapun, objek dibandingkan dengan referensi, dan referensi ke someValue berubah dengan setiap rendering. Ada ide?
Penggunaan dasar
Nilai yang dikembalikan oleh doSomething dapat disimpan menggunakan useMemo. Ini akan mencegah rendering yang tidak perlu.
Kode:
const BasicMemo = () => {
const [age, setAge] = useState(19);
const handleClick = () => setAge(age < 100 ? age + 1 : age);
const someValue = () => ({ value: "some value" });
const doSomething = useMemo(() => someValue, []);
return (
<>
<Age age={age} handleClick={handleClick} />
<Guide doSomething={doSomething} />
</>
);
};
const Age = ({ age, handleClick }) => {
return (
<div>
<p> {age} .</p>
<p> </p>
<button onClick={handleClick}> !</button>
</div>
);
};
const Guide = React.memo((props) => {
const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
return (
<div style={{ background: color, padding: ".4rem" }}>
<p style={{ color: color, filter: "invert()" }}>
.
</p>
</div>
);
});
Bak pasir:
useRef
useRef mengembalikan objek ref. Nilai objek ini tersedia melalui properti "saat ini". Properti ini dapat diberi nilai awal: useRef (initialValue). Objek ref ada selama umur komponen.
Mengakses DOM
Kode:
const DomAccess = () => {
const textareaEl = useRef(null);
const handleClick = () => {
textareaEl.current.value =
" - , . , !";
textareaEl.current.focus();
};
const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
return (
<>
<button onClick={handleClick}>
.
</button>
<label htmlFor="msg">
.
</label>
<textarea ref={textareaEl} id="msg" />
</>
);
};
Bak pasir:
Variabel seperti instance (generik)
Objek ref dapat berisi nilai apa pun, bukan hanya penunjuk ke elemen DOM.
Kode:
const StringVal = () => {
const textareaEl = useRef(null);
const stringVal = useRef(
" - , . , !"
);
const handleClick = () => {
textareaEl.current.value = stringVal.current;
textareaEl.current.focus();
};
const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
return (
<>
<button onClick={handleClick}>
.
</button>
<label htmlFor="msg">
.
</label>
<textarea ref={textareaEl} id="msg" />
</>
);
};
Bak pasir:
useRef bisa digunakan untuk menyimpan ID timer agar bisa dihentikan nanti.
Kode:
const IntervalRef = () => {
const [time, setTime] = useState(0);
const setIntervalRef = useRef();
useEffect(() => {
const id = setInterval(() => {
setTime((time) => (time = new Date().toLocaleTimeString()));
}, 1000);
setIntervalRef.current = id;
return () => clearInterval(setIntervalRef.current);
}, [time]);
return (
<>
<p> :</p>
<time>{time}</time>
</>
);
};
Bak pasir:
Saya harap Anda menikmati artikelnya. Terima kasih atas perhatian Anda.