استفاده از مقادیر بولی isLoading را متوقف کنید
“Using status enums به جای booleans” را در egghead.io تماشا کنید
«Handling Errors HTTP with React» را در egghead.io تماشا کنید
(بخشی از راهنمای ReactJS مبتدیان)
بیا با هم بازی کنیم
API مکان جغرافیایی
کمی و در مورد خطرات آن بیاموزید isLoading مقادیر بولی (و مقادیر بولی مشابه
به عنوان: isRejected، isIdle، یا isResolved) در حالی که ما آنجا هستیم. استفاده خواهم کرد
برای نشان دادن این موضوع واکنش نشان دهید، اما این مفاهیم به طور کلی برای هر چارچوب یا چارچوبی اعمال می شود
زبان
function geoPositionReducer(state, action) {
switch (action.type) {
case 'error': {
return {
...state,
isLoading: false,
error: action.error,
}
}
case 'success': {
return {
...state,
isLoading: false,
position: action.position,
}
}
default: {
throw new Error(`Unhandled action type: ${action.type}`)
}
}
}
function useGeoPosition() {
const [state, dispatch] = React.useReducer(geoPositionReducer, {
isLoading: true,
position: null,
error: null,
})
React.useEffect(() => {
if (!navigator.geolocation) {
dispatch({
type: 'error',
error: new Error('Geolocation is not supported'),
})
return
}
const geoWatch = navigator.geolocation.watchPosition(
(position) => dispatch({ type: 'success', position }),
(error) => dispatch({ type: 'error', error }),
)
return () => navigator.geolocation.clearWatch(geoWatch)
}, [])
return state
}
این چیزهای بسیار استاندارد برای کدهایی مانند این است. من این را در هر دو دیده ام
برنامه ها و کتابخانه های متن باز در همه جا (واکنش یا نه). با این حال، وجود دارد
برخی از مشکلات با این رویکرد وجود دارد. هیچ ایده ای دارید که آنها چه می توانند باشند؟ اجازه دهید
من به شما نشان خواهم داد که چگونه یک توسعه دهنده می تواند از این قلاب خاص استفاده کند:
function YourPosition() {
const { isLoading, position, error } = useGeoPosition()
if (isLoading) {
return div>Loading your position...div>
}
if (position) {
return (
div>
Lat: {position.coords.latitude}, Long: {position.coords.longitude}
div>
)
}
if (error) {
return (
div>
div>Oh no, there was a problem getting your position:div>
pre>{error.message}pre>
div>
)
}
}
آیا هنوز می توانید مشکل را شناسایی کنید؟ بله عالی! نه؟ مشکلی نیست، بزار کمکت کنم
یک سناریوی واقعی دنیای واقعی
تصور کنید چه اتفاقی می افتد اگر یک کاربر به داخل ماشین بپرد و شروع به رانندگی کند
در اطراف و دستگاه با شکست
GeolocationPositionError.POSITION_UNAVAILABLE یا
GeolocationPositionError.TIMEOUT یا حتی اگر کاربر غیرفعال کردن را انتخاب کند
مجوز مکان جغرافیایی برای برنامه شما و شما یک
GeolocationPositionError.PERMISSION_DENIED. آن وقت چه اتفاقی می افتاد؟ اس
همانطور که کامپوننت در بالا نوشته شده است، ما همیشه آخرین موقعیت ثبت شده را نمایش خواهیم داد
و هرگز پیغام خطا را به کاربر نشان ندهد!
اگر ما چیزها را تغییر دادیم و فقط در صورتی موقعیت را نشان دادیم که خطایی وجود نداشته باشد، پس
ما مشکل برعکس خواهیم داشت: ما فقط یک خطا را نشان خواهیم داد، حتی یک خطای بعدی
درخواست های موفق برای این موقعیت
چندین راه وجود دارد که می توانیم این را تغییر دهیم تا از این مشکل جلوگیری کنیم:
- مطمئن شوید که کاربران قلاب همیشه موقعیت و خطا را در صورت وجود نشان می دهند
موجود است. - پاک کن
errorزمانی که گرفتن موقعیت موفق و واضح است
positionاگر خطایی وجود داشته باشد - یک ویژگی اضافی را نشان می دهد که وضعیت فعلی را نشان می دهد
اطلاعات موقعیت جغرافیایی
به عنوان خالق چیزی قابل استفاده مجدد، همیشه آسان تر است که آن را برای مردم قابل استفاده مجدد کنید
من نمی توانم انجام کار اشتباه حتی اگر آنها من می خواهم به یا حداقل طبیعی تر است
انجام کار درست به جای کار اشتباه (گودال موفقیت و همه چیز). و همینطور
گزینه شماره 1 حذف شد
برای گزینه شماره 2، ممکن است افرادی وجود داشته باشند که بخواهند جدیدترین ها را نشان دهند
موقعیت را حتی در صورت وجود خطا (و پیام خطا را نیز در آن نشان دهید
این مورد) پس این کار نخواهد کرد.
پس بیایید به گزینه شماره 3 برویم:
function geoPositionReducer(state, action) {
switch (action.type) {
case 'error': {
return {
...state,
status: 'rejected',
error: action.error,
}
}
case 'success': {
return {
...state,
status: 'resolved',
position: action.position,
}
}
case 'started': {
return {
...state,
status: 'pending',
}
}
default: {
throw new Error(`Unhandled action type: ${action.type}`)
}
}
}
function useGeoPosition() {
const [state, dispatch] = React.useReducer(geoPositionReducer, {
status: 'idle',
position: null,
error: null,
})
React.useEffect(() => {
if (!navigator.geolocation) {
dispatch({
type: 'error',
error: new Error('Geolocation is not supported'),
})
return
}
dispatch({ type: 'started' })
const geoWatch = navigator.geolocation.watchPosition(
(position) => dispatch({ type: 'success', position }),
(error) => dispatch({ type: 'error', error }),
)
return () => navigator.geolocation.clearWatch(geoWatch)
}, [])
return state
}
متوجه خواهید شد که ما ارسال دیگری را اضافه کرده ایم تا بتوانیم خود را از آن متمایز کنیم
idle و pending کشورها در این مورد خاص، واقعاً وجود ندارد
تفاوت بین این دو (قبل از اینکه شروع کنیم isLoading همانطور که درست است
که در واقع به این معنی است که) اما مواردی وجود دارد که تشخیص
بین این دو حالت پایانی مهم است و من نگران هستم که اگر آن را وارد نکنم
کسی این را کپی/پیست می کند و این نکته مهم را از دست می دهد.
عالی، اکنون کاربران می توانند استفاده کنند status به جای رندر کردن:
function YourPosition() {
const { status, position, error } = useGeoPosition()
if (status === 'idle' || status === 'pending') {
return div>Loading your position...div>
}
if (status === 'resolved') {
return (
div>
Lat: {position.coords.latitude}, Long: {position.coords.longitude}
div>
)
}
if (status === 'rejected') {
return (
div>
div>Oh no, there was a problem getting your position:div>
pre>{error.message}pre>
div>
)
}
// could also use a switch or nested ternary if that's your jam...
}
با آن در کدهای سندباکس اینجا بازی کنید
با کمک یک status متغیر، نه ساده isLoading ما را نشان می دهد
به کاربران ما این امکان را می دهد که دقیقاً بدانند وضعیت در هر مقطع زمانی مشخص چیست.
حالا اگر می خواهید آنها را رها کنید variable === 'string' عبارات در اینها
if عبارات به این دلیل که دوست دارید چقدر مختصر باشد isBoolean خوب نترس
به اندازه کافی ساده است:
const { status, position, error } = useGeoPosition()
const isLoading = status === 'idle' || status === 'pending'
const isResolved = status === 'resolved'
const isRejected = status === 'rejected'
و ممکن است کسی استدلال کند که می توانید این متغیرها را در حالت کاهنده خود ذخیره کنید
به جای استخراج مقادیر آنها. اما این
شما را آسیب پذیر می کند
حالت های غیر ممکن! اما اگر واقعا
شما می خواهید که کاربران شما مجبور به استفاده نباشند variable === 'string'، سپس فقط انجام دهید
شما قطعا به آن پایبند هستید status در ایالت خود مطمئن شوید که فقط یک مورد دارید
مقدار نهایی حالت ممکن است و سپس می توانید حالت های بولی مانند a را استخراج کنید
راحتی اگر می خواهید:
function useGeoPosition() {
// ... clipped out for brevity ...
return {
isLoading: status === 'idle' || status === 'pending',
isIdle: status === 'idle',
isPending: status === 'pending',
isResolved: status === 'resolved',
isRejected: status === 'rejected',
...state,
}
}
همه چیز یک ماشین حالت است و این یکی به ویژه قابل توجه است.
XState یک کتابخانه عالی برای نمایش وضعیت است
ماشینها در کد هستند، بنابراین فکر کردم به شما نشان دهم که این چه چیزی میتواند باشد:
import { createMachine, assign } from 'xstate'
import { useMachine } from '@xstate/react'
const context = { position: null, error: null }
const RESOLVE = {
target: 'resolved',
actions: 'setPosition',
}
const REJECT = {
target: 'rejected',
actions: 'setError',
}
const geoPositionMachine = createMachine(
{
id: 'geoposition',
initial: 'idle',
context,
states: {
idle: {
on: {
START: 'pending',
REJECT_NOT_SUPPORTED: 'rejectedNotSupported',
},
},
pending: {
on: { RESOLVE, REJECT },
},
resolved: {
on: { RESOLVE, REJECT },
},
rejected: {
on: { RESOLVE, REJECT },
},
rejectedNotSupported: {},
},
},
{
actions: {
setPosition: assign({
position: (context, event) => event.position,
}),
setError: assign({
error: (context, event) => event.error,
}),
},
},
)
function useGeoPosition() {
const [state, send] = useMachine(geoPositionMachine)
React.useEffect(() => {
if (!navigator.geolocation) {
send('REJECT_NOT_SUPPORTED')
return
}
send('START')
const geoWatch = navigator.geolocation.watchPosition(
(position) => send({ type: 'RESOLVE', position }),
(error) => send({ type: 'REJECT', error }),
)
return () => navigator.geolocation.clearWatch(geoWatch)
}, [send])
return state
}
با آن در کدهای سندباکس اینجا بازی کنید
ساختن این را در جریان زندهام تماشا کنید
ابتدا اگر با ماشین های state یا xstate آشنایی ندارید، مراقب باشید
تعصب خانوادگی در برابر این کد. امروز ممکن است برای شما آسان نباشد،
اما درست مانند هر انتزاعی، با گذشت زمان واضح تر می شود
با او خرج می کنی
در مورد این مثال به چند نکته توجه خواهید کرد. من یک وضعیت ویژه برای
زمانی که موقعیت جغرافیایی پشتیبانی نمی شود. این ایالت هیچ “کنترل کننده رویداد” نداردبه آن متصل است و بنابراین “حالت پایانی” نامیده می شود. این برای
مورد استفاده ما زیرا اگر پشتیبانی نشود، تغییر به آن غیرممکن است
هر کشور دیگری و دستگاه دولتی ما تضمین می کند که این هرگز اتفاق نخواهد افتاد.
اما بیشتر از آن، من می خواهم توجه داشته باشید که دیگر وجود ندارد status
دولتی که باید حفظ کنیم را status حالت اکنون تنها بخشی از حالت ماشین است
مقدار حالت نهایی بنابراین status است تعبیه شده در ماشین، نه
به طور جداگانه مدیریت می شود.
در اینجا نحوه استفاده از آن (با حداقل تغییرات ممکن) آمده است:
function YourPosition() {
const state = useGeoPosition()
const { position, error } = state.context
if (state.matches('rejectedNotSupported')) {
return div>This browser does not support Geolocationdiv>
}
if (state.matches('idle') || state.matches('pending')) {
return div>Loading your position...div>
}
if (state.matches('resolved')) {
return (
div>
Lat: {position.coords.latitude}, Long: {position.coords.longitude}
div>
)
}
if (state.matches('rejected')) {
return (
div>
div>Oh no, there was a problem getting your position:div>
pre>{error.message}pre>
div>
)
}
}
و اگر API قبلی را دوست داشتیم، میتوانیم این API را نگه داریم:
function useGeoPosition() {
const [state, send] = useMachine(geoPositionMachine)
// ... clipped out for brevity ...
return { status: state.value, ...state.context }
}
شما شخصاً ممکن است مثال کاهش دهنده را ساده تر یا قابل درک تر از آن بیابید
ماشین دولتی اگر با ماشین های state یا xstate آشنایی ندارید، پس
این قابل درک است من به شایستگی ماشین های دولتی نمی پردازم
این پست، اما من مدتی پیش در مورد آنها نوشتم:
پیاده سازی یک کتابخانه ماشین حالت ساده در جاوا اسکریپت.
اوه و نگاه کن
این اجرا توسط
دیوید خورشید (نویسنده xstate) که در آن
به طور کامل نیاز را برطرف می کند useEffect و به جای آن اشتراک را قرار می دهد
منطق در خود دستگاه!!! این عالی است زیرا به این معنی است که ما
موتور کاملاً چارچوب آگنوستیک است و می تواند مستقل از رابط کاربری کار کند
چارچوبی که از آن استفاده می کنید (یا حتی اگر خود را ساخته باشید). او حتی در یک
تایم اوت (فقط برای سرگرمی). خیلی چیزهای جالب اینجاست
در پایان، امیدوارم ارزش تعریف آن را بدانید
حالت های لبه ای که در کد خود دارید و با انجام این کار از خطا جلوگیری کنید. این نیست
زمین برای ماشین آلات با شرایط محدود به همان اندازه که درخواست کاهش ماست
با تکیه بر مقادیر بولی که نمی توانند تمام حالت های واقعی را که کد ما می تواند نشان دهد
در هر لحظه باشد از یک ماشین حالت یا enum استفاده کنید. این یک بولی نیست. متشکرم
شما و موفق باشید!


