Fungsi dalam komponen stateless?

90

Saya mencoba untuk mengubah <canvas>animasi keren yang saya temukan di sini menjadi komponen React yang dapat digunakan kembali. Sepertinya komponen ini memerlukan satu komponen induk untuk kanvas, dan banyak komponen anak untuk function Ball().

Untuk alasan kinerja, mungkin akan lebih baik untuk menjadikannya Ballskomponen tanpa negara karena jumlahnya akan banyak. Saya tidak begitu familiar dengan membuat komponen stateless dan bertanya-tanya di mana saya harus mendefinisikan this.update()dan this.drawfungsi yang didefinisikan function Ball().

Apakah fungsi untuk komponen stateless masuk ke dalam atau ke luar komponen? Dengan kata lain, manakah dari berikut ini yang lebih baik?

1:

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

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

    return (
       ...
    );
}

2:

function update() {
     ...
}

function draw() {
     ...
}

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

Apa pro / kontra dari masing-masing dan apakah salah satunya lebih baik untuk kasus penggunaan tertentu seperti saya?

MarksCode
sumber
Bisakah Anda memposting kode yang sudah ada sehingga kami melihat bagaimana kode itu akan digunakan?
Scimonster
@Scimonster Saya mempostingnya di tautan yang disematkan, mungkin Anda melewatkannya. Ini tautannya: codepen.io/awendland/pen/XJExGv
MarksCode

Jawaban:

113

Hal pertama yang perlu diperhatikan adalah bahwa komponen fungsional stateless tidak dapat memiliki metode, Anda tidak boleh mengandalkan panggilan updateatau drawrender Balljika itu adalah komponen fungsional stateless.

Dalam kebanyakan kasus, Anda harus mendeklarasikan fungsi di luar fungsi komponen sehingga Anda mendeklarasikannya hanya sekali dan selalu menggunakan kembali referensi yang sama. Saat Anda mendeklarasikan fungsi di dalamnya, setiap kali komponen dirender, fungsi tersebut akan ditentukan lagi.

Ada beberapa kasus di mana Anda perlu mendefinisikan fungsi di dalam komponen untuk, misalnya, menetapkannya sebagai pengendali kejadian yang berperilaku berbeda berdasarkan properti komponen. Tapi tetap saja Anda bisa mendefinisikan fungsi di luar Balldan mengikatnya dengan properti, membuat kode lebih bersih dan membuat fungsi updateatau drawdapat digunakan kembali.

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

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

Jika Anda menggunakan hook , Anda bisa menggunakan useCallbackuntuk memastikan fungsi hanya didefinisikan ulang ketika salah satu dependensinya ( props.xdalam kasus ini) berubah:

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

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

Ini cara yang salah :

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

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

Saat menggunakan useCallback, mendefinisikan updatefungsi dalam useCallbackkait itu sendiri di luar komponen menjadi keputusan desain lebih dari apa pun, Anda harus mempertimbangkan jika Anda akan menggunakan kembali updatedan / atau jika Anda perlu mengakses ruang lingkup penutupan komponen, misalnya, baca / tulis negara. Secara pribadi saya memilih untuk mendefinisikannya di dalam komponen secara default dan membuatnya dapat digunakan kembali hanya jika diperlukan, untuk mencegah rekayasa berlebihan dari awal. Selain itu, menggunakan kembali logika aplikasi lebih baik dilakukan dengan hook yang lebih spesifik, meninggalkan komponen untuk tujuan presentasi. Mendefinisikan fungsi di luar komponen saat menggunakan hook benar-benar bergantung pada nilai decoupling dari React yang Anda inginkan untuk logika aplikasi Anda.

Marco Scabbiolo
sumber
Terima kasih Marco, itu menjelaskan sedikit. Hal yang membuat saya bingung dalam kasus saya terkait dengan this.drawfungsi di dalam file Ball. Ini menggunakan ctxfrom apa yang akan menjadi induk <canvas>dan juga menggunakan thiskata kunci untuk apa yang akan menjadi Ballkomponen anak . Apa cara terbaik untuk mengintegrasikan implementasi komponen stateless sehingga kedua properti tersebut dapat diakses?
MarksCode
tidak ada thissaat menggunakan komponen fungsional tanpa negara, ingatlah itu. Untuk konteks kanvas, Anda harus meneruskannya ke setiap single Ball, kedengarannya tidak bagus sama sekali.
Marco Scabbiolo
Jadi dalam hal ini akan lebih baik jika tidak ada komponen anak yang Anda maksud?
MarksCode
1
@MarcoScabbiolo tidak tidak, itu bukan kasus saya, sudah menggunakan fungsi panah secara native untuk waktu yang cukup lama, karena satu-satunya browser yang tidak mendukungnya adalah IE. Sebenarnya saya berhasil menemukan komentar ini dari satu artikel di mana sebenarnya diklaim bahwa bindsecara khusus di Chrome lebih awal dari 59 bahkan lebih lambat dari fungsi panah. Dan di Firefox cukup lama karena keduanya bekerja dengan kecepatan yang sama. Jadi saya akan mengatakan dalam kasus seperti itu tidak ada perbedaan cara mana yang disukai :)
Vadim Kalinin
1
@ MauricioAvendaño bagaimanapun caranya bekerja, tetapi merupakan praktik yang buruk bagi Somethingkomponen untuk mengetahui bahwa ada prop X di komponen induknya karena membuatnya sadar akan konteksnya. Hal yang sama terjadi untuk pertanyaan yang Anda tanyakan dan kode contoh yang saya tulis, itu tergantung pada konteksnya, yang diabaikan demi kesederhanaan.
Marco Scabbiolo
12

Kita dapat memiliki fungsi di dalam komponen fungsional tanpa kewarganegaraan, berikut contohnya:

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

Namun, ini bukan praktik yang baik karena fungsi handlePick()akan ditentukan setiap kali komponen dipanggil.

Ruchir Saxena
sumber
11
Jika ini bukan praktik yang baik, lalu apa solusi alternatifnya?
John Samuel
@JohnSamuel Alternatifnya adalah dengan mendefinisikan di handlePick()atas / di luar komponen seperti function handlePick() { ... }; const Action = () => { ... }hasil yang handlePick hanya didefinisikan sekali. Jika Anda membutuhkan data dari Actionkomponen, Anda harus meneruskannya sebagai parameter ke handlePickfungsi.
James Hay
12
jika ini bukan praktik yang baik, Anda dapat menambahkan praktik yang baik dengan jawabannya? sekarang saya harus mencari lagi praktik yang baik :(
Shamseer Ahammed
2

Kita dapat menggunakan hook React useCallbackseperti di bawah ini dalam komponen fungsional:

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;

Saya mendapatkan 'img' dari induknya dan itu adalah sebuah array.

Vishant Rastogi
sumber
0

Jika Anda ingin menggunakan props atau status komponen dalam fungsi, itu harus didefinisikan dalam komponen dengan useCallback.

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

Di sisi lain, jika Anda tidak ingin menggunakan props atau state in function, tentukan di luar komponen.

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

function Component(props){
  
}
Hossein Mohammadi
sumber
3
dapatkah Anda memberikan contoh penggunaan metode hook di dalam komponen fungsional? (metode tidak ditetapkan negara)
jake-ferguson