پراپ فعال کننده

در اکثر مواقع به پراپ فعال کننده احتیاجی نیست و برای مخفی و ظاهر کردن عنصر متحرک ، فقط نیاز هست که یک 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}
      />
    </>
  )
}

عنصر مرجع