Durum bilgisi olmayan bileşenlerde işlevler?


93

Burada<canvas> bulduğum bu harika animasyonu bir React yeniden kullanılabilir bileşenine dönüştürmeye çalışıyorum . Görünüşe göre bu bileşen, tuval için bir ana bileşen ve çok sayıda alt bileşen gerektirecekfunction Ball() .

Performans nedenlerinden ötürü, Ballsbirçoğu olacağı için , büyük olasılıkla vatansız bileşenlere dönüştürmek daha iyi olacaktır. Vatansız bileşenler yapmaya o kadar aşina değilim ve burada tanımlanan işlevleri this.update()ve tanımlamam gerektiğini merak ediyordum this.draw.function Ball() .

Durum bilgisi olmayan bileşenlere ilişkin işlevler bileşenin içine mi yoksa dışına mı giriyor? Başka bir deyişle, aşağıdakilerden hangisi daha iyidir?

1:

const Ball = (props) => {
    const update = () => {
        ...
    }

    const draw = () => {
        ...
    }

    return (
       ...
    );
}

2:

function update() {
     ...
}

function draw() {
     ...
}

const Ball = (props) => {
    return (
       ...
    );
}

Her birinin artıları / eksileri nelerdir ve benimki gibi belirli kullanım durumları için bunlardan biri daha mı iyidir?


Nasıl kullanılacağını görmemiz için mevcut kodu gönderebilir misiniz?
Scimonster

@Scimonster Gömülü bir bağlantıya gönderdim, belki kaçırdınız. İşte bağlantı: codepen.io/awendland/pen/XJExGv
MarksCode

Yanıtlar:


117

Unutulmaması gereken ilk şey, durum bilgisi olmayan işlevsel bileşenlerin yöntemlere sahip olamayacağıdır, arama yapmaya updateveya drawoluşturulmuş birBall , bir durum bilgisi olmayan işlevsel bileşense bir bileşene güvenmemelisiniz.

Çoğu durumda, işlevleri bileşen işlevinin dışında bildirmeniz gerekir, böylece onları yalnızca bir kez bildirirsiniz ve her zaman aynı referansı yeniden kullanırsınız. İçindeki işlevi bildirdiğinizde, bileşen her oluşturulduğunda işlev yeniden tanımlanacaktır.

Örneğin, bileşenin özelliklerine bağlı olarak farklı şekilde davranan bir olay işleyicisi olarak atamak için bileşenin içinde bir işlev tanımlamanız gereken durumlar vardır. Ancak yine de işlevi dışarıda tanımlayabilir Ballve özelliklerine bağlayarak kodu daha temiz hale getirebilir ve updateveya drawişlevlerini yeniden kullanılabilir hale getirebilirsiniz .

// You can use update somewhere else
const update (propX, a, b) => { ... };

const Ball = props => (
  <Something onClick={update.bind(null, props.x)} />
);

Kancalar kullanıyorsanız useCallback, işlevin yalnızca bağımlılıklarından biri ( props.xbu durumda) değiştiğinde yeniden tanımlandığından emin olmak için kullanabilirsiniz :

const Ball = props => {
  const onClick = useCallback((a, b) => {
    // do something with a, b and props.x
  }, [props.x]);

  return (
    <Something onClick={onClick} />
  );
}

Bu yanlış yol :

const Ball = props => {
  function update(a, b) {
    // props.x is visible here
  }

  return (
    <Something onClick={update} />
  );
}

Kullanırken useCallback, kancadaki updateişlevi tanımlamak, useCallbackbileşen dışında her şeyden çok bir tasarım kararı haline gelir, yeniden kullanacaksanız updateve / veya bileşenin kapanış kapsamına erişmeniz gerekip gerekmediğini hesaba katmalısınız. örneğin, eyalete okuma / yazma. Şahsen ben onu varsayılan olarak bileşenin içinde tanımlamayı seçiyorum ve baştan itibaren aşırı mühendisliği önlemek için yalnızca ihtiyaç duyulduğunda yeniden kullanılabilir hale getiriyorum. Bunun da ötesinde, uygulama mantığının yeniden kullanılması, bileşenleri sunum amaçlı bırakarak daha özel kancalarla daha iyi yapılır. Kancaları kullanırken, işlevi bileşen dışında tanımlamak, gerçekten uygulama mantığınız için istediğiniz React'ten ayırma derecesine bağlıdır.


Teşekkürler Marco, bu işleri biraz düzeltir. Benim durumumda kafam karışan şey, this.drawiçindeki işlevle ilgili Ball. Kullandığı ctxebeveynin ne olacağını dan <canvas>da ve kullanan thisçocuk ne olacağını gösteren anahtar kelime Ballbileşeni. Bu özelliklerin her ikisinin de erişilebilir olması için durum bilgisiz bileşeni uygulamanın entegre etmenin en iyi yolu nedir?
MarksCode

thisvatansız işlevsel bileşenleri kullanırken hiçbir şey yoktur , bunu aklınızda tutun. Kanvas bağlamı için, onu her bir parçaya aktarmanız gerekir Ball, bu hiç de iyi gelmiyor.
Marco Scabbiolo

Öyleyse bu durumda, söylediğiniz hiçbir çocuk bileşeninin olmaması en iyisi mi?
MarksCode

1
@MarcoScabbiolo hayır hayır, bu benim durumum değil, zaten ok işlevlerini oldukça uzun bir süredir yerel olarak kullanıyor, çünkü onları desteklemeyen tek tarayıcı IE. Aslında bu yorumu , özellikle Chrome'da 59'dan önce ok işlevlerinden bile daha yavaş olduğu iddia edilen bir makaleden bulmayı başardım . Ve Firefox'ta, ikisi de aynı hızda performans gösterdikleri için oldukça uzun bir süredir. Bu yüzden böyle durumlarda tercih edilen yolun ne olduğu konusunda hiçbir fark olmadığını söyleyebilirim :)bind
Vadim Kalinin

1
@ MauricioAvendaño her iki şekilde de çalışır, ancak Somethingbileşenin kendi bağlamından haberdar olmasını sağladığı için üst bileşeninde bir destek X olduğunu bilmesi kötü bir uygulamadır . Aynısı, sorduğunuz soru ve yazdığım örnek kod için de geçerlidir, basitlik uğruna göz ardı edilen içeriğe bağlıdır.
Marco Scabbiolo

13

Durumsuz işlevsel bileşenler içinde işlevlere sahip olabiliriz, aşağıdaki örnek:

 const Action = () => {
  function  handlePick(){
     alert("test");
  }
  return (
    <div>
      <input type="button" onClick={handlePick} value="What you want to do ?" />
    </div>
  );
}

Ancak, handlePick()bileşen her çağrıldığında işlev tanımlanacağı için bu iyi bir uygulama değildir .


11
Bu iyi bir uygulama değilse, alternatif çözüm ne olacak?
John Samuel

@JohnSamuel Bunun alternatifi, sonucun handlePick'in yalnızca bir kez tanımlanması handlePick()gibi bileşenin yukarısını / dışını tanımlamaktır function handlePick() { ... }; const Action = () => { ... }. Bileşenden veriye ihtiyacınız varsa Action, bunu handlePickfonksiyona parametre olarak iletmelisiniz .
James Hay

14
Bu iyi bir uygulama değilse, yanıta iyi uygulamayı da ekleyebilirdiniz? şimdi iyi uygulama için tekrar
aramam gerekiyor

2

React kancasını useCallbackaşağıdaki gibi işlevsel bir bileşende kullanabiliriz:

const home = (props) => {
    const { small, img } = props
    const [currentInd, setCurrentInd] = useState(0);
    const imgArrayLength = img.length - 1;
    useEffect(() => {
        let id = setInterval(() => {
            if (currentInd < imgArrayLength) {
                setCurrentInd(currentInd => currentInd + 1)
            }
            else {
                setCurrentInd(0)
            }
        }, 5000);
        return () => clearInterval(id);
    }, [currentInd]);
    const onLeftClickHandler = useCallback(
        () => {
            if (currentInd === 0) {

            }
            else {
                setCurrentInd(currentInd => currentInd - 1)
            }
        },
        [currentInd],
    );

    const onRightClickHandler = useCallback(
        () => {
            if (currentInd < imgArrayLength) {
                setCurrentInd(currentInd => currentInd + 1)
            }
            else {

            }
        },
        [currentInd],
    );
    return (
        <Wrapper img={img[currentInd]}>
            <LeftSliderArrow className={currentInd > 0 ? "red" : 'no-red'} onClick={onLeftClickHandler}>
                <img src={Icon_dir + "chevron_left_light.png"}></img>
            </LeftSliderArrow>
            <RightSliderArrow className={currentInd < imgArrayLength ? "red" : 'no-red'} onClick={onRightClickHandler}>
                <img src={Icon_dir + "chevron_right_light.png"}></img>
            </RightSliderArrow>
        </Wrapper>);
}

export default home;

Ebeveyninden 'img' alıyorum ve bu bir dizi.


1

Fonksiyonda bileşen veya bileşen durumunu kullanmak istiyorsanız, bu useCallback ile bileşende tanımlanmalıdır.

function Component(props){
  const onClick=useCallback(()=>{
     // Do some things with props or state
  },[])
}

Öte yandan, işlevde props veya state kullanmak istemiyorsanız, bunu bileşenin dışında tanımlayın.

const computeSomethings=()=>{
   // Do some things with params or side effects
}

function Component(props){
  
}

3
işlevsel bileşen içinde yöntem için kanca kullanımına bir örnek verebilir misiniz? (durum yöntemi ayarlanmadı)
jake-ferguson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.