useSyncExternalStore ਇੱਕ ਕੰਪੋਨੈਂਟ ਨੂੰ React ਤੋਂ ਬਾਹਰ ਰਹਿਣ ਵਾਲਾ ਡੇਟਾ ਪੜ੍ਹਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ, ਬਿਨਾਂ ਉਹਨਾਂ ਵਿਜ਼ੂਅਲ ਗਲਤੀਆਂ (visual glitches) ਦੇ ਜੋ concurrent rendering ਕਾਰਨ ਹੋ ਸਕਦੀਆਂ ਹਨ। ਇਹ useEffect ਨਾਲ ਬਾਹਰੀ ਮੁੱਲਾਂ ਨੂੰ local state ਵਿੱਚ ਲਿਆਉਣ ਦੇ ਪੁਰਾਣੇ ਤਰੀਕੇ ਨੂੰ ਬਦਲਦਾ ਹੈ, ਜੋ ਕਿ "tearing" ਦਾ ਕਾਰਨ ਬਣ ਸਕਦਾ ਹੈ – ਜਿੱਥੇ UI ਦੇ ਹਿੱਸੇ ਇੱਕੋ ਰੈਂਡਰ ਦੌਰਾਨ ਵੱਖ-ਵੱਖ ਮੁੱਲ ਦਿਖਾਉਂਦੇ ਹਨ।
Concurrency ਦੇ ਅਧੀਨ ਪੁਰਾਣਾ ਤਰੀਕਾ ਕਿਉਂ ਟੁੱਟਦਾ ਹੈ
React 18 ਤੋਂ ਪਹਿਲਾਂ, ਆਮ ਤਰੀਕਾ ਇਹ ਸੀ:
const [value, setValue] = useState(initial);
useEffect(() => {
const unsubscribe = externalSource.subscribe(v => setValue(v));
return unsubscribe;
}, []);
Effect ਕੰਪੋਨੈਂਟ ਦੇ ਰੈਂਡਰ ਹੋਣ ਤੋਂ ਬਾਅਦ ਚੱਲਦਾ ਹੈ। ਜਦੋਂ React ਦਾ concurrent renderer ਰੈਂਡਰ ਨੂੰ ਰੋਕਣ, ਰੱਦ ਕਰਨ ਜਾਂ ਦੁਬਾਰਾ ਚਲਾਉਣ ਦਾ ਫੈਸਲਾ ਕਰਦਾ ਹੈ, ਤਾਂ effect ਉਸ ਸਮੇਂ ਤੋਂ ਵੱਖਰੇ ਬਿੰਦੂ 'ਤੇ ਚੱਲ ਸਕਦਾ ਹੈ ਜਦੋਂ UI ਸਟੇਟ ਨੂੰ ਪੜ੍ਹ ਰਿਹਾ ਹੁੰਦਾ ਹੈ। ਨਤੀਜਾ ਇੱਕ ਮਿਸਮੈਚ (mismatch) ਹੁੰਦਾ ਹੈ: ਇੱਕ ਕੰਪੋਨੈਂਟ ਨੇ ਪੁਰਾਣਾ ਮੁੱਲ ਪੜ੍ਹਿਆ ਹੋ ਸਕਦਾ ਹੈ, ਦੂਜੇ ਨੇ ਨਵਾਂ, ਅਤੇ ਸਕ੍ਰੀਨ ਕੁਝ ਸਮੇਂ ਲਈ ਅਸੰਗਤ ਡੇਟਾ ਦਿਖਾਉਂਦੀ ਹੈ। ਇਹ "tearing" ਪੂਰੀ ਤਰ੍ਹਾਂ ਸਿੰਕਰੋਨਸ (synchronous) ਦੁਨੀਆ ਵਿੱਚ ਨੁਕਸਾਨਦੇਹ ਨਹੀਂ ਹੈ, ਪਰ ਇਹ ਇੱਕ ਦਿਖਾਈ ਦੇਣ ਵਾਲਾ ਬੱਗ (bug) ਬਣ ਜਾਂਦੀ ਹੈ ਜਦੋਂ React ਇੱਕੋ ਸਮੇਂ UI ਦੇ ਕਈ ਵਰਜ਼ਨਾਂ ਨੂੰ ਰੈਂਡਰ ਕਰਦਾ ਹੈ।
useSyncExternalStore ਸਮੱਸਿਆ ਨੂੰ ਕਿਵੇਂ ਹੱਲ ਕਰਦਾ ਹੈ
ਇਸ ਹੁੱਕ (hook) ਨੂੰ ਦੋ ਕਾਲਬੈਕਸ (callbacks) ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ:
- subscribe – ਇੱਕ ਲਿਸਨਰ ਰਜਿਸਟਰ ਕਰਦਾ ਹੈ ਅਤੇ ਇੱਕ ਫੰਕਸ਼ਨ ਵਾਪਸ ਕਰਦਾ ਹੈ ਜੋ ਇਸਨੂੰ ਹਟਾ ਦਿੰਦਾ ਹੈ।
- getSnapshot – ਬਾਹਰੀ ਸਰੋਤ (external source) ਦਾ ਮੌਜੂਦਾ ਮੁੱਲ ਵਾਪਸ ਕਰਦਾ ਹੈ।
React ਕੰਪੋਨੈਂਟ ਮਾਊਂਟ (mount) ਹੋਣ ਵੇਲੇ subscribe ਨੂੰ ਕਾਲ ਕਰਦਾ ਹੈ ਅਤੇ ਅਨਮਾਊਂਟ (unmount) ਹੋਣ 'ਤੇ ਇਸਨੂੰ ਖਤਮ ਕਰ ਦਿੰਦਾ ਹੈ। ਹਰ ਰੈਂਡਰ ਦੌਰਾਨ ਇਹ getSnapshot ਨੂੰ ਕਾਲ ਕਰਦਾ ਹੈ ਅਤੇ ਇਹ ਗਾਰੰਟੀ ਦਿੰਦਾ ਹੈ ਕਿ ਇੱਕੋ ਬਾਹਰੀ ਸਟੋਰ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵਾਲੇ ਸਾਰੇ ਕੰਪੋਨੈਂਟ ਬਿਲਕੁਲ ਇੱਕੋ ਸਨੈਪਸ਼ੌਟ (snapshot) ਪੜ੍ਹਦੇ ਹਨ। ਕਿਉਂਕਿ ਪੜ੍ਹਨ ਦੀ ਪ੍ਰਕਿਰਿਆ ਰੈਂਡਰ ਫੇਜ਼ ਦੇ ਅੰਦਰ ਹੁੰਦੀ ਹੈ, ਰੈਂਡਰਰ ਪੂਰੇ ਟ੍ਰੀ (tree) ਵਿੱਚ ਅੱਪਡੇਟਸ ਨੂੰ ਕੋਆਰਡੀਨੇਟ ਕਰ ਸਕਦਾ ਹੈ, ਜਿਸ ਨਾਲ tearing ਖਤਮ ਹੋ ਜਾਂਦੀ ਹੈ।
Zustand ਅਤੇ Redux ਵਰਗੀਆਂ ਲਾਇਬ੍ਰੇਰੀਆਂ ਪਹਿਲਾਂ ਹੀ ਆਪਣੇ ਸਟੋਰਾਂ ਨੂੰ ਇਸ ਹੁੱਕ ਨਾਲ ਲਪੇਟਦੀਆਂ (wrap) ਹਨ, ਇਸੇ ਲਈ ਉਹ React 18 ਦੇ concurrent ਫੀਚਰਾਂ ਨਾਲ ਤਿਆਰ ਮਿਲਦੀਆਂ ਹਨ।
ਉਹ ਨਿਯਮ ਜੋ ਹੁੱਕ ਨੂੰ ਗਲਤ ਹੋਣ ਤੋਂ ਰੋਕਦੇ ਹਨ
- ਸਿਰਫ਼ ਉਹੀ ਵਾਪਸ ਕਰੋ ਜਿਸਦੀ ਕੰਪੋਨੈਂਟ ਨੂੰ ਲੋੜ ਹੈ – ਇੱਕ ਵੱਡਾ ਆਬਜੈਕਟ React ਨੂੰ ਹਰ ਅੱਪਡੇਟ 'ਤੇ ਡੂੰਘੇ ਢਾਂਚੇ (deep structures) ਦੀ ਤੁਲਨਾ ਕਰਨ ਲਈ ਮਜਬੂਰ ਕਰਦਾ ਹੈ। ਇੱਕ ਸੀਮਤ (narrow) snapshot ਬਦਲਾਅ ਦੀ ਪਛਾਣ ਨੂੰ ਸਸਤਾ ਰੱਖਦਾ ਹੈ।
- Referential identity ਨੂੰ ਬਰਕਰਾਰ ਰੱਖੋ – ਜੇਕਰ ਬਾਹਰੀ ਮੁੱਲ ਨਹੀਂ ਬਦਲਿਆ ਹੈ, ਤਾਂ getSnapshot ਨੂੰ ਉਹੀ ਆਬਜੈਕਟ ਰੈਫਰੈਂਸ ਵਾਪਸ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ। ਹਰ ਕਾਲ 'ਤੇ ਇੱਕ ਨਵਾਂ ਆਬਜੈਕਟ ਵਾਪਸ ਕਰਨ ਨਾਲ ਇੱਕ ਅਨੰਤ ਰੈਂਡਰ ਲੂਪ (infinite render loop) ਸ਼ੁਰੂ ਹੋ ਜਾਂਦਾ ਹੈ ਕਿਉਂਕਿ React ਹਰ ਰੈਂਡਰ 'ਤੇ ਬਦਲਾਅ ਦੇਖਦਾ ਹੈ।
- ਵਰਤੋਂ ਨੂੰ ਸਿਰਫ਼ ਬਾਹਰੀ ਸਰੋਤਾਂ ਤੱਕ ਸੀਮਤ ਰੱਖੋ – ਲੋਕਲ ਕੰਪੋਨੈਂਟ ਸਟੇਟ
useStateਜਾਂuseReducerਵਿੱਚ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ। ਸਿਰਫ਼ ਅੰਦਰੂਨੀ ਮੁੱਲਾਂ ਲਈ external-store ਹੁੱਕ ਦੀ ਵਰਤੋਂ ਕਰਨ ਨਾਲ ਬੇਲੋੜੀ ਗੁੰਝਲਦਾਰਤਾ ਵਧਦੀ ਹੈ।
useSyncExternalStore ਦੀ ਵਰਤੋਂ ਕਦੋਂ ਕਰਨੀ ਚਾਹੀਦੀ ਹੈ
- ਬ੍ਰਾਊਜ਼ਰ APIs ਨੂੰ ਲਪੇਟਣਾ (window size, network-online status, media queries)।
- ਇੱਕ ਸਾਧਾਰਨ JavaScript ਸਟੋਰ ਨੂੰ ਕਨੈਕਟ ਕਰਨਾ ਜੋ React ਬਾਰੇ ਨਹੀਂ ਜਾਣਦਾ।
- Prop-drilling ਨੂੰ ਇੱਕ ਗਲੋਬਲ ਰੀਡ-ਓਨਲੀ ਸਰੋਤ ਨਾਲ ਬਦਲਣਾ ਜਿਸ ਨਾਲ ਬਹੁਤ ਸਾਰੇ ਕੰਪੋਨੈਂਟ ਸਬਸਕ੍ਰਾਈਬ ਕਰਦੇ ਹਨ।
ਵਿਰੋਧੀ ਦਲੀਲ (The counter-argument)
useState + useEffect ਦਾ ਤਰੀਕਾ ਅਜੇ ਵੀ ਸਧਾਰਨ, ਘੱਟ ਬਦਲਣ ਵਾਲੇ ਡੇਟਾ ਲਈ ਕੰਮ ਕਰਦਾ ਹੈ, ਖਾਸ ਕਰਕੇ ਜਦੋਂ ਕੋਈ ਐਪਲੀਕੇਸ਼ਨ concurrent ਫੀਚਰਾਂ ਨੂੰ ਇਨੇਬਲ ਨਹੀਂ ਕਰਦੀ। ਇਹ ਇੱਕ ਲੋਅਰ-ਲੈਵਲ ਪਹੁੰਚ ਹੈ ਜਿਸ ਬਾਰੇ ਕੁਝ ਡਿਵੈਲਪਰਾਂ ਨੂੰ ਸਮਝਣਾ ਆਸਾਨ ਲੱਗਦਾ ਹੈ ਕਿਉਂਕਿ subscription ਲੌਜਿਕ ਸਪੱਸ਼ਟ ਤੌਰ 'ਤੇ effect ਵਿੱਚ ਹੁੰਦੀ ਹੈ। ਹਾਲਾਂਕਿ, ਇਸ ਦਾ ਨੁਕਸਾਨ ਇਹ ਹੈ ਕਿ ਇੱਕ ਵਾਰ concurrent rendering ਚਾਲੂ ਹੋਣ 'ਤੇ tearing ਦਾ ਖਤਰਾ ਰਹਿੰਦਾ ਹੈ। ਉਹਨਾਂ ਪ੍ਰੋਜੈਕਟਾਂ ਲਈ ਜੋ ਪਹਿਲਾਂ ਹੀ React 18 ਦੀਆਂ ਨਵੀਆਂ ਸਮਰੱਥਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਹਨ, useSyncExternalStore ਦੀ ਵਾਧੂ ਸੁਰੱਖਿਆ ਬਹੁਤ ਘੱਟ ਵਾਧੂ ਬੋਇਲਰਪਲੇਟ (boilerplate) ਦੇ ਮੁਕਾਬਲੇ ਵਧੇਰੇ ਫਾਇਦੇਮੰਦ ਹੈ।
ਅੱਗੇ ਕੀ ਦੇਖਣਾ ਹੈ
- ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਸਟੋਰ ਦਾ getSnapshot ਇੱਕ ਸਥਿਰ (stable) ਰੈਫਰੈਂਸ ਵਾਪਸ ਕਰਦਾ ਹੈ; ਡਿਵੈਲਪਮੈਂਟ ਦੌਰਾਨ ਰੈਫਰੈਂਸ ਦਾ ਇੱਕ ਤੇਜ਼
console.logਅਣਜਾਣੇ ਵਿੱਚ ਆਬਜੈਕਟ ਦੇ ਮੁੜ-ਬਣਾਉਣ (recreation) ਨੂੰ ਫੜ ਸਕਦਾ ਹੈ। - Subscription ਫੰਕਸ਼ਨ ਨੂੰ ਹਲਕਾ (lightweight) ਰੱਖੋ – ਭਾਰੀ ਕੰਮ ਬਾਹਰੀ ਸਰੋਤ ਦੇ ਅੰਦਰ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ, ਨਾ ਕਿ React ਨੂੰ ਦਿੱਤੇ ਗਏ ਕਾਲਬੈਕ ਵਿੱਚ।
- React ਦੇ concurrent mode ਦੇ ਅਧੀਨ ਕੰਪੋਨੈਂਟਾਂ ਦਾ ਟੈਸਟ ਕਰੋ (ਉਦਾਹਰਨ ਲਈ,
ReactDOM.createRootਦੇ ਨਾਲactਦੀ ਵਰਤੋਂ ਕਰਕੇ) ਤਾਂ ਜੋ ਇਹ ਪੁਸ਼ਟੀ ਕੀਤੀ ਜਾ ਸਕੇ ਕਿ ਕੋਈ tearing ਨਹੀਂ ਦਿਖ ਰਹੀ।
ਇੱਕ ਲਾਈਵ ਡੈਮੋ ਹੁੱਕ ਨੂੰ ਕੰਮ ਕਰਦੇ ਹੋਏ ਦਿਖਾਉਂਦਾ ਹੈ, ਜੋ React ਤੋਂ ਬਾਹਰ ਸਟੋਰ ਕੀਤੇ ਇੱਕ ਸਧਾਰਨ ਕਾਊਂਟਰ ਨੂੰ ਸਿੰਕ ਕਰਦਾ ਹੈ ਅਤੇ ਵਿਜ਼ੂਅਲ ਗਲਤੀਆਂ ਤੋਂ ਬਿਨਾਂ ਕਈ ਕੰਪੋਨੈਂਟਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰਦਾ ਹੈ। ਡੈਮੋ https://usesyncexternalstore.vercel.app/ 'ਤੇ ਉਪਲਬਧ ਹੈ। ਸੋਰਸ ਕੋਡ GitHub 'ਤੇ ਹੈ, ਅਤੇ ਇੱਕ ਲੰਬਾ ਲੇਖ ਇਸਦੇ ਇੰਪਲੀਮੈਂਟੇਸ਼ਨ ਵੇਰਵਿਆਂ ਦੀ ਵਿਆਖਿਆ ਕਰਦਾ ਹੈ।
Takeaway: ਇੱਕ ਅਜਿਹੀ ਦੁਨੀਆ ਵਿੱਚ ਜਿੱਥੇ React UI ਦੇ ਹਿੱਸਿਆਂ ਨੂੰ ਗਲਤ ਕ੍ਰਮ ਵਿੱਚ ਰੈਂਡਰ ਕਰ ਸਕਦਾ ਹੈ, useSyncExternalStore ਬਾਹਰੀ ਡੇਟਾ ਅਤੇ ਕੰਪੋਨੈਂਟ ਰੈਂਡਰਿੰਗ ਦੇ ਵਿਚਕਾਰ ਇੱਕ ਭਰੋਸੇਯੋਗ ਪੁਲ ਹੈ। ਇਹ ਵਿਜ਼ੂਅਲ ਇਕਸਾਰਤਾ ਨੂੰ ਬਰਕਰਾਰ ਰੱਖਦਾ ਹੈ, ਗਲੋਬਲ ਸਟੋਰਾਂ ਤੱਕ ਵਿਸਤਾਰ ਕਰਦਾ ਹੈ, ਅਤੇ ਪਹਿਲਾਂ ਹੀ ਪ੍ਰਸਿੱਧ ਸਟੇਟ-ਮੈਨੇਜਮੈਂਟ ਲਾਇਬ੍ਰੇਰੀਆਂ ਵਿੱਚ ਸ਼ਾਮਲ ਹੈ। ਜਿੱਥੇ ਵੀ ਤੁਹਾਨੂੰ non-React ਸਟੇਟ ਪੜ੍ਹਨ ਦੀ ਲੋੜ ਹੋਵੇ, ਇਸ ਨੂੰ ਅਪਣਾਓ, ਅਤੇ ਇਸਦੇ ਪੂਰੇ ਲਾਭ ਲੈਣ ਲਈ ਸਨੈਪਸ਼ੌਟਸ ਨੂੰ ਛੋਟਾ ਅਤੇ ਸਥਿਰ ਰੱਖੋ।
