ReactJS: Lembar Cheat Hooks





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 .



  1. Mendownload repositori
  2. Instal dependensi: npm i
  3. 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.



All Articles