Hello anh em, chắc khi làm việc với ReactJs thì chúng ta cũng đã làm quen với việc sử dụng các hooks của nó rồi phải không, vậy thì hôm nay mình sẽ cùng anh em biết và đã biết tìm hiểu lại một chút về thằng hook useEffect() nhé.
useEffect() sinh ra mục đích để quản lý vòng đời của của một component và nó phục vụ chúng ta sử dụng trong function component thay vì các lifecycle như trước đây trong class component (về cơ bản là giống nhau). useEffect() lắng nghe các thay đổi của biến và state trong array phụ thuộc, khi giá trị của chúng thay đổi thì phần thân trong useEffect() sẽ được thực thi.
return trong hook này được sử dụng để clean các methods đã chạy. Lần đầu thiên hook này được gọi thì phần thân của nó sẽ được đánh giá đầu tiên. Các lần tiếp theo và tiếp theo nữa các câu lệnh return trả về sẽ được đánh giá đầu tiên và sau đó mới là phân thân chính của hook. Điều này đặc biệt có ích trong việc clean code đã chạy trước khi chạy lại, ngăn chặn việc rò rỉ bộ nhớ.
Có một điều thú vị là khi chúng ta sử dụng các non-primittive data types Javascript ( arrays, objects, functions ) làm phụ thuộc, với các primitive value như integers và strings, chúng ta có thể difine một biến từ một biến khác :
const a =1const b =1
a === b
// Output: true
Nhưng đối với các giá trị non-primittive, ví dụ như object thì nó lại khác :
{}==={}// Output: false
Vì vậy chúng ta cần phải rất cẩn thận trong việc sử dụng các object làm đối tượng phụ thuộc, bởi vì bằng mắt thường nhìn thì chúng ta có thể thấy rằng object này không thay đổi nhưng thật ra là không phải vậy. Thay vì sửa dụng các object, chúng ta có thể sử dụng các properties làm giá trị phụ thuộc, chẳng hạn :
useEffect(()=>{// đoạn code sử dụng properties của anh em},[myObject.property1, myObject.property2]);
Sơ sơ là như vậy, giờ chúng ta cùng đi vào các trường hợp sử dụng của useEffect() nhé :
- Chạy một lần khi mount : tìm nạp data API.
- Chạy khi thay đổi state : thường thì khi validate input đầu vào.
- Chạy khi thay đổi state : filtering trực tiếp.
- Chạy khi thay đổi state : trigger animation trên giá trị của array mới.
- Chạy khi props thay đổi : update lại list đã fetched API khi data update.
- Chạy khi props thay đổi : updateing data API để cập nhật BTC
Chạy một lần khi mount : tìm nạp data API.
Khi chúng ta muốn thực hiện hành động này một lần, đặc biệt là khi sử dụng loads hoặc mounts, có thể sử dụng useEffect() để thực hiện. Trong trường hợp cụ thể dưới đây, chúng ta gọi fetch() yêu cầu GET khi ứng dụng được mount, sử dụng một array rỗng làm giá trị phụ thuộc:
import{ useState, useEffect }from"react";constUseCaseFetchApi=props=>{const[bio, setBio]=useState({});useEffect(()=>{constfetchData=async()=>{const response =awaitfetch('https://api/people/1/');const data =await response.json();
console.log(data);setBio(data);};fetchData();},[]);return(<><hr /><h2>useEffect use case</h2><h3>Running once on mount: fetch API data</h3><p>bio:</p><pre>{JSON.stringify(bio,null,'t')}</pre></>);};exportdefault UseCaseFetchApi;
Chạy khi thay đổi state : thường thì khi validate input đầu vào.
Xác thực đầu vào cũng là một trường hợp khá hay. Trong khi value đầu vào lưu ở useState, việc xác thực input đầu vào mỗi khi thay đổi, đưa ra phản hồi ngay lập tức cho người dùng.
Chúng ta có thể thêm một hàm setTimeout() để kiểm tra input sau một thời gian, để trì hoãn việc kiểm tra mỗi lần gõ phím của người dùng, chúng ta sẽ cần xóa bộ đếm thời gian đó bằng cách sử dụng clearTimeout() hàm trong return của useEffect() :
import{ useEffect, useState }from"react";constUseCaseInputValidation=props=>{const[input, setInput]=useState('');const[isValid, setIsValid]=useState(false);constinputHandler=e=>{setInput(e.target.value);};useEffect(()=>{if(input.length <5||/d/.test(input)){setIsValid(false);}else{setIsValid(true);}},[input]);return(<><hr /><h2>useEffect use case</h2><h3>Running on state change: validating input field</h3><form><label htmlFor="input">Write something(more than 5 non numerical characters is a valid input)</label><br /><input type="text" id="input" autoComplete="off" onChange={inputHandler} style={{ height:'1.5rem', width:'20rem', marginTop:'1rem'}}/></form><p><span style={isValid ?{ backgroundColor:'lightgreen', padding:'.1px'}:{ backgroundColor:'lightpink', padding:'.1px'}}>{isValid ?'Valid input':'Input not valid'}</span></p></>);};exportdefault UseCaseInputValidation;
Chạy khi thay đổi state : filtering trực tiếp.
Chúng ta có thể sử dụng useEffect() để lọc một mảng bằng cách nhập các chữ cái của một phần tử đầu vào. Để làm như vậy, chúng ta sẽ cần sử dụng một trạng thái để lưu dữ liệu đầu vào và việc triển khai bộ lọc bên trong useEffect() sẽ được kích hoạt khi đầu vào thay đổi, nhờ vào các useEffect() phụ thuộc.
import{ useEffect, useState }from"react";const array =[{ key:'1', type:'planet', value:'Tatooine'},{ key:'2', type:'planet', value:'Alderaan'},{ key:'3', type:'starship', value:'Death Star'},{ key:'4', type:'starship', value:'CR90 corvette'},{ key:'5', type:'starship', value:'Star Destroyer'},{ key:'6', type:'person', value:'Luke Skywalker'},{ key:'7', type:'person', value:'Darth Vader'},{ key:'8', type:'person', value:'Leia Organa'},];constUseCaseLiveFilter=props=>{const[inputValue, setInputValue]=useState('');const[inputType, setInputType]=useState('');const[filteredArray, setFilteredArray]=useState(array);constinputValueHandler=e=>{setInputValue(e.target.value);};constinputTypeHandler=e=>{setInputType(e.target.value);};useEffect(()=>{setFilteredArray((_)=>{const newArray = array.filter(item=> item.value.includes(inputValue)).filter(item=> item.type.includes(inputType));return newArray;});},[inputValue, inputType]);// Prepare array to be renderedconst listItems = filteredArray.map((item)=><><tr><td style={{ border:'1px solid lightgray', padding:'0 1rem'}}>{item.type}</td><td style={{ border:'1px solid lightgray', padding:'0 1rem'}}>{item.value}</td></tr ></>);return(<><hr /><h2>useEffect use case</h2><h3>Running on state change: live filtering</h3><form style={{ maxWidth:'23rem', display:'flex', justifyContent:'space-between', alignItems:'center'}}><div><label htmlFor="input-type">Filter by <b>type</b></label><br /><input type="text" id="input-type" autoComplete="off" onChange={inputTypeHandler} style={{ height:'1.5rem', width:'10rem', marginTop:'1rem'}}/></div><div><label htmlFor="input-value">Filter by <b>value</b></label><br /><input type="text" id="input-value" autoComplete="off" onChange={inputValueHandler} style={{ height:'1.5rem', width:'10rem', marginTop:'1rem'}}/></div></form><br /><table style={{ width:'20rem', border:'1px solid gray', padding:'0 1rem'}}><tr><th>Type</th><th>Value</th></tr>{listItems}</table></>);};exportdefault UseCaseLiveFilter;
Chạy khi thay đổi state : trigger animation trên giá trị của array mới.
Chúng ta có thể sử dụng useEffect() để kích hoạt animation trên giỏ hàng khi thêm sản phẩm mới vào đó. Trong trường hợp này, chúng ta sẽ cần mộtstate để xử lý các mặt hàng trong giỏ hàng và một state khác để xử lý khi kích hoạt animation.
import{ useState, useEffect }from'react';import classes from'./UseCaseAnimation.module.css';const products =['Death Star','CR90 corvette','Millennium Falcon','X-wing fighter','TIE fighter'];constUseCaseAnimation=props=>{const[cart, setCart]=useState([]);const[triggerAnimation, setTriggerAnimation]=useState(false);// Add item to the cart (array)constclickHandler=e=>{
e.preventDefault();setCart(prevCart=>{const newCart =[...prevCart];
newCart.push(e.target.value);return newCart;});};// Clear the cart (array)constclearHandler=e=>{
e.preventDefault();setCart([]);};// Trigger cart animationuseEffect(()=>{setTriggerAnimation(true);const timer =setTimeout(()=>{setTriggerAnimation(false);},900);// The duration of the animation defined in the CSS file// Clear the timer before setting a new onereturn()=>{clearTimeout(timer);};},[cart]);const cartClasses = triggerAnimation ?`${classes['jello-horizontal']}${classes.cart}`: classes.cart;const itemsOnSale = products.map(itemOnSale=>{return<li><form><span className={classes.item}>{itemOnSale}<button onClick={clickHandler} value={`"${itemOnSale}"`}>Add to cart</button></span></form></li >;});const cartItems = cart.map(item=>{return<li>{item}</li>;});return(<><hr /><h2>useEffect use case</h2><h3>Running on state change: trigger animation on newarray value</h3><h4 style={{ color:'blue'}}>Starship Marketplace</h4><ul>{itemsOnSale}</ul><div className={cartClasses}><span>Cart</span></div><div><p>Elements in cart:</p><ul>{cartItems}</ul></div><form><button className={classes.margin} onClick={clearHandler} value="clear">Clear cart</button></form></>);};exportdefault UseCaseAnimation;
Chạy khi props thay đổi : update lại list đã fetched API khi data update.
Trong trường hợp sử dụng này, chúng ta muốn kích hoạt cập nhật state khi gọi fetch(). Chúng ta đang gửi dữ liệu đã tìm nạp đến một component con và bất cứ khi nào dữ liệu đó được thay đổi, component con sẽ xử lý lại nó.
import{ useState, useEffect, useCallback }from"react";constBaconParagraphs=props=>{const[baconParagraphText, setBaconParagraphText]=useState([]);useEffect(()=>{setBaconParagraphText(props.chopBacon.map(piece=><p key={Math.random()}>{piece}</p>));},[props.chopBacon]);// Propsreturn(<><p>Number of paragraphs:{baconParagraphText.length}</p>{baconParagraphText}</>);};constUseCaseUpdateFetch=()=>{const[bacon, setBacon]=useState([]);const[isLoading, setIsLoading]=useState(false);constsubmitHandler=asynce=>{
e.preventDefault();setIsLoading(true);const response =awaitfetch(`https://baconipsum.com/api/?type=all-meat¶s=${e.target.paragraphs.value}&start-with-lorem=1`);const data =await response.json();setIsLoading(false);setBacon(data);};return(<><hr /><h2>useEffect use case</h2><h3>Running on props change: update paragraph list on fetched API data update</h3><form onSubmit={submitHandler}><label htmlFor="paragraphs" style={{ display:"block", marginBottom:"1rem"}}>How many paragraphs of"Bacon ipsum"do you want?</label><select id="paragraphs" name="paragraphs"><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option></select><input type="submit" value="Show me the bacon!" style={{ marginLeft:"1rem"}}/>{isLoading &&<span>Getting paragraphs... 🐷</span>}</form><BaconParagraphs chopBacon={bacon}/></>);};exportdefault UseCaseUpdateFetch;
Chạy khi props thay đổi : updateing data API để cập nhật BTC
Trong ví dụ này, useEffect() được sử dụng để tìm nạp dữ liệu mới từ một API sau mỗi 3 giây. Thành phần con useEffect() nhận thời gian là phụ thuộc và mỗi khi phụ thuộc đó thay đổi, một thành phần mới fetch() được kích hoạt. Bằng cách này, chúng ta có thể có tỷ giá hối đoái BTC cập nhật trong ứng dụng của mình.
import{ useState, useEffect }from"react";import classes from'./UseCaseUpdateApi.module.css';// SECTION - FunctionsconstgetCurrentTime=()=>{const now =newDate();const time = now.getHours()+':'+('0'+ now.getMinutes()).slice(-2)+':'+('0'+ now.getSeconds()).slice(-2);return time;};// SECTION - ComponentsconstExchangeRate=props=>{const[exchangeRate, setExchangeRate]=useState(0);const[isAnimated, setIsAnimated]=useState(false);useEffect(()=>{constgetExchangeRate=async()=>{const response =awaitfetch("api của anh em");const data =await response.json();
console.log(data.find(item=> item.currency ==="BTC").rate);setExchangeRate(data.find(item=> item.currency ==="BTC").rate);};getExchangeRate();// Triggering animationsetIsAnimated(true);const classTimer =setTimeout(()=>{setIsAnimated(false);},1500);// Clear the timer before setting a new onereturn()=>{clearTimeout(classTimer);setExchangeRate(exchangeRate);};},[props.onTime]);const priceClasses = isAnimated ?`${classes.price}${classes.heartbeat}`:`${classes.price}`;return<div className={priceClasses}>USD<b>{exchangeRate}</b></div>;};constUseCaseUpdateApi=props=>{const[time, setTime]=useState(getCurrentTime());// Trigger the update interval on startup (mount)useEffect(()=>{const interval =setInterval(()=>{setTime(getCurrentTime());},3000);return()=>clearInterval(interval);},[]);
console.log(time);return(<><hr /><h2>useEffect use case</h2><h3>Running on props change: updating fetched API data to get updated BTC price</h3><span>Last updated:{time}(polling every 3 seconds)</span><ExchangeRate onTime={time}/></>);};exportdefault UseCaseUpdateApi;
Kết bài
Vậy là bài viết đến đây là kết thúc rồi.
Mong là nó sẽ giúp ich cho anh em nhiều hơn
Nếu thấy hay hãy like, share và upvote cho mình nhé.
Nguồn: viblo.asia