Функції компонентів без стану?


90

Я намагаюся перетворити цю класну <canvas>анімацію, яку я знайшов тут, на багаторазовий компонент React. Схоже, для цього компонента потрібен один батьківський компонент для полотна та багато дочірніх компонентів для function Ball().

З міркувань продуктивності, мабуть, було б краще перетворити Ballsкомпоненти без громадянства, оскільки їх буде багато. Я не настільки знайомий з виготовленням компонентів без стану, і мені було цікаво, де мені визначити функції this.update()та this.drawфункції, які визначені в function Ball().

Функції для компонентів без стану переходять всередину компонента або зовні? Іншими словами, що з наведеного краще?

1:

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

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

    return (
       ...
    );
}

2:

function update() {
     ...
}

function draw() {
     ...
}

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

Які плюси / мінуси кожного та чи є один із них кращим для конкретних випадків використання, таких як мій?


Чи можете ви опублікувати існуючий код, щоб ми побачили, як він буде використаний?
Scimonster

@Scimonster Я опублікував це у вбудованому посиланні, можливо, ви це пропустили. Ось посилання: codepen.io/awendland/pen/XJExGv
MarksCode

Відповіді:


113

Перше, на що слід звернути увагу, це те, що функціональні компоненти без громадянства не можуть мати методів, ви не повинні розраховувати на виклик updateабо drawна надання, Ballякщо це функціональний компонент без стану.

У більшості випадків вам слід оголосити функції поза функцією компонента, тому ви оголошуєте їх лише один раз і завжди використовуєте одне і те ж посилання. Коли ви оголошуєте функцію всередині, кожен раз, коли компонент відображається, функція буде визначена знову.

Бувають випадки, коли вам потрібно буде визначити функцію всередині компонента, щоб, наприклад, призначити її як обробник подій, яка поводиться по-різному залежно від властивостей компонента. Але все-таки ви можете визначити функцію зовні Ballі прив'язати її до властивостей, зробивши код набагато чистішим і зробивши функції updateабо drawбагаторазовими.

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

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

Якщо ви використовуєте хуки , ви можете використовувати, useCallbackщоб перевизначити функцію лише тоді, коли одна із залежностей ( props.xу даному випадку) змінюється:

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

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

Це неправильний шлях :

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

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

Під час використання useCallback, визначення updateфункції в самому useCallbackгачку нашого зовнішнього компонента стає дизайнерським рішенням більше, ніж будь-що, ви повинні враховувати, якщо ви збираєтеся повторно використовувати updateта / або якщо вам потрібно отримати доступ до області закриття компонента, наприклад, читати / писати в штат. Особисто я вирішив визначити його всередині компонента за замовчуванням і зробити його багаторазовим лише за потреби, щоб запобігти надмірному проектуванню з самого початку. Крім того, повторне використання логіки додатків краще робити за допомогою більш конкретних хуків, залишаючи компоненти для презентаційних цілей. Визначення функції поза компонентом під час використання хуків насправді залежить від ступеня роз'єднання з React, який ви хочете для вашої логіки програми.


Дякую Марко, це трохи прояснює ситуацію. Те, що мене бентежить у моєму випадку, пов’язане з this.drawфункцією всередині Ball. Він використовує ctxз того, що буде батьківським, <canvas>а також використовує thisключове слово для того, що буде дочірнім Ballкомпонентом. Який найкращий спосіб інтегрувати реалізацію компонента без громадянства, щоб обидва ці властивості були доступними?
MarksCode

немає thisпри використанні функціональних компонентів без стану, майте це на увазі. Для контексту полотна вам доведеться передати його кожному Ball, що зовсім не добре звучить.
Marco Scabbiolo

Тож у цьому випадку було б найкраще просто не мати дочірнього компонента, про який ви говорите?
MarksCode

1
@MarcoScabbiolo ні, ні, це не мій випадок, коли я вже давно використовую функції стрілок, оскільки єдиним браузером, який їх не підтримує, є IE. Насправді мені вдалося знайти цей коментар з однієї статті, де насправді стверджується, що bindсаме в Chrome раніше 59 було навіть повільніше, ніж функції стрілок. І у Firefox це теж досить давно, оскільки вони обидва виступають з однаковою швидкістю. Тож я б сказав, що в таких випадках немає різниці, який
Вадим Калінін

1
@ MauricioAvendaño в будь-якому випадку працює, але погана практика для Somethingкомпонента знати, що в його батьківському компоненті є prop X, оскільки він усвідомлює його контекст. Те саме відбувається з питанням, яке ви задаєте, і зразком коду, який я написав, це залежить від контексту, який ігнорується заради простоти.
Марко Скаббіоло

13

Ми можемо мати функції всередині функціональних компонентів без стану, нижче наведено приклад:

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

Але це не є доброю практикою, оскільки функція handlePick()визначається кожного разу, коли компонент викликається.


11
Якщо це не є доброю практикою, то яким буде альтернативне рішення?
Джон Самуель

@JohnSamuel Альтернатива полягає у визначенні handlePick()вище / поза компонентом, як function handlePick() { ... }; const Action = () => { ... }результат того, що handlePick визначається лише один раз. Якщо вам потрібні дані від Actionкомпонента, ви повинні передати їх як параметри handlePickфункції.
Джеймс Хей,

12
якщо це не найкраща практика, ви могли б додати належну практику відповіддю? тепер мені доведеться знову шукати хорошу практику :(
Шамсір Ахаммед

2

Ми можемо використовувати гак React, useCallbackяк показано нижче, у функціональному компоненті:

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;

Я отримую 'img' від його батьківського, і це масив.


0

Якщо ви хочете використовувати у функції функцію props або стан компонента, це слід визначити в компоненті з useCallback.

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

З іншого боку, якщо ви не хочете використовувати пропс або стан у функції, визначте це за межами компонента.

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

function Component(props){
  
}

3
Ви можете навести приклад використання хука для методу всередині функціонального компонента? (метод не встановлено)
jake-ferguson
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.