پراپ فعال کننده
در اکثر مواقع به پراپ فعال کننده احتیاجی نیست و برای مخفی و ظاهر کردن عنصر متحرک ، فقط نیاز هست که یک boolean را به عنوان state مقدار دهی کنید.
const [active, setActive] = useState(false)
<ElementPopper
element={<Element />}
popper={active && <Popper />}
/>
ولی در برخی مواقع مثلا مواقعی که به حجم وسیعی از عملیات برای ساخته شدن کامپوننت عنصر متحرک احتیاج است و به صرفه نیست که برای هر بار ظاهر شدن عنصر متحرک دوباره اون عملیات تکرار بشه ، یا در مواقعی که طول و ارتفاع عنصر متحرک در لحظه ظاهر شدنش مشخص نیست (مثلا طول و عرض بعد از عملیات async مشخص میشه ) لازم هست که برای ظاهر و مخفی شدن عنصر متحرک از پراپ active استفاده کنید . در غیر اینصورت ممکنه که موقعیت عنصر متحرک به درستی محاسبه نشه .
function AsyncPopper() {
const [element, setElement] = useState()
useEffect(() => {
//async operation
setTimeout(() => {
setElement(
<div
style={{
width: "120px",
height: "120px",
backgroundColor: "white"
}}
>
Popper Element
</div>
)
}, 200);
}, [])
return element || <div>Loading ...</div>
}
const [active, setActive] = useState(false)
<ElementPopper
element={<Element />}
popper={<AsyncPopper />}
active={active}
/>
اما به ندرت مواردی اتفاق می افته که هیچکدام از دو مثال بالا جوابگو نیست. در این مواقع پیشنهاد میشه، از ترکیب دو مثال بالا استفاده کنید.
در این قسمت سعی کردم برای هرکدوم از این سه موقعیت گفته شده یک مثال عملی بیارم:
مثال اول
در این مثال از پراپ active استفاده نشده و برای اکثر مواقع کاربرد داره.
در واقع با استفاده از یک boolean به عنوان state، عنصر متحرک در همون لحظه ظاهر شدن تولید میشه.
import React, { useState } from "react"
import ElementPopper from "react-element-popper"
function Component({ height, width, backgroundColor, children }) {
return (
<div
style={{
width: width + "px",
height: height + "px",
backgroundColor,
textAlign: "center",
display: "flex",
flexDirection: "column",
justifyContent: "center"
}}
>
{children}
</div>
)
}
export default function Example() {
const [active, setActive] = useState(false)
return (
<>
<button
onClick={() => setActive(!active)}
>
نمایش/عدم نمایش
</button>
<br />
<ElementPopper
element={(
<Component
height={40}
width={120}
backgroundColor="red"
>
Refrence Element
</Component>
)}
popper={active && (
<Component
height={120}
width={120}
backgroundColor="gray"
>
Popper Element
</Component>
)}
position="left"
/>
</>
)
}
مثال دوم
من در فانکشن بالا یک تغییر ایجاد کردم تا خروجیش رو با تاخیر برگردونه و همانطور که میبینید اگر با روش مثال اول بخواهیم از اون استفاده کنیم، محاسبات موقعیت آن با خطا همراه میشه.
function AsyncComponent({ height, width, backgroundColor, children }) {
const [props, setProps] = useState()
useEffect(() => {
setProps({
style: {
width: width + "px",
height: height + "px",
backgroundColor,
textAlign: "center",
display: "flex",
flexDirection: "column",
justifyContent: "center"
}
})
}, [height, width, backgroundColor])
return props ?
<div {...props}>
{children}
</div>
:
null
}
export default function Example() {
const [active, setActive] = useState(false)
return (
<>
<button
onClick={() => setActive(!active)}
>
نمایش/عدم نمایش
</button>
<br />
<ElementPopper
element={(
<Component
height={40}
width={120}
backgroundColor="red"
>
Refrence Element
</Component>
)}
popper={active && (
<AsyncComponent
height={120}
width={120}
backgroundColor="gray"
>
Popper Element
</AsyncComponent>
)}
position="left"
/>
</>
)
}
دلیل بوجود اومدن این خطای محاسباتی اینه که در لحظه اول، به جای div ، مقدار null به عنوان عنصر متحرک تعریف میشه و همون باعث به وجود اومدن این مشکل میشه.
برای برطرف کردن این مشکل میتونید خیلی راحت از پراپ active استفاده کنید:
export default function Example() {
const [active, setActive] = useState(false)
return (
<>
<button
onClick={() => setActive(!active)}
>
نمایش/عدم نمایش
</button>
<br />
<ElementPopper
element={(
<Component
height={40}
width={120}
backgroundColor="red"
>
Refrence Element
</Component>
)}
popper={(
<AsyncComponent
height={120}
width={120}
backgroundColor="gray"
>
Popper Element
</AsyncComponent>
)}
position="left"
active={active}
/>
</>
)
}
همانطور که میبینید دیگه اشکالی در محاسبه موقعیت عنصر متحرک وجود نداره و دلیلشم اینه که که در حقیقت، عنصر متحرک در لحظه ظاهر شدن تولید نمیشه و همیشه وجود داره، و شما با تغییر مقدار active، فقط visibility اونو از حالت hidden به visible تغییر میدید.
مثال سوم
برخی موقع هام پیش میاد که کامپوننت شما مثل مثال دوم تعریف شده، ولی شما میخواید اونو مثل مثال اول ظاهر کنید.
در این صورت باید از ترکیبی از مثال اول و دوم استفاده کنید:
function AsyncComponent({ height, width, backgroundColor, children, onReady }) {
const [state, setState] = useState({})
useEffect(() => {
setState({
props: {
style: {
width: width + "px",
height: height + "px",
backgroundColor,
textAlign: "center",
display: "flex",
flexDirection: "column",
justifyContent: "center"
}
},
ready: true
})
}, [height, width, backgroundColor])
useEffect(() => {
if (!state.ready) return
onReady()
}, [state.ready, onReady])
return state.ready ?
<div {...state.props}>
{children}
</div>
:
null
}
export default function Example() {
const [active, setActive] = useState(false)
const [isPopperReady, setIsPopperReady] = useState(false)
return (
<>
<button
onClick={() => {
if (!active) {
setActive(true)
} else {
setActive(false)
setIsPopperReady(false)
}
}}
>
نمایش/عدم نمایش
</button>
<br />
<ElementPopper
element={(
<Component
height={40}
width={120}
backgroundColor="red"
>
Refrence Element
</Component>
)}
popper={active && (
<AsyncComponent
height={120}
width={120}
backgroundColor="gray"
onReady={() => setIsPopperReady(true)}
>
Popper Element
</AsyncComponent>
)}
position="left"
active={isPopperReady}
/>
</>
)
}