Функции в компонентах без состояния?

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 (
       ...
    );
}

Каковы плюсы и минусы каждого из них и один из них лучше для конкретных случаев использования, таких как мой?

MarksCode
источник
Можете ли вы опубликовать существующий код, чтобы мы увидели, как он будет использоваться?
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, что совсем не очень хорошо.
Марко Скаббиоло
Так что в этом случае было бы лучше просто не иметь дочернего компонента, о котором вы говорите?
MarksCode
1
@MarcoScabbiolo нет, нет, это не мой случай, я уже довольно давно использую стрелочные функции изначально, поскольку единственный браузер, не поддерживающий их, - это IE. На самом деле мне удалось найти этот комментарий из одной статьи, где фактически утверждалось, что bindконкретно в Chrome до 59 года было даже медленнее, чем функции стрелок. И в Firefox тоже довольно долго, поскольку они оба работают с одинаковой скоростью. Так что я бы сказал, что в таких случаях нет разницы, какой путь предпочтительнее :)
Вадим Калинин
1
@ MauricioAvendaño в любом случае работает, но это плохая практика для Somethingкомпонента знать, что в его родительском компоненте есть свойство X, поскольку он сообщает ему о своем контексте. То же самое происходит с вопросом, который вы задаете, и с образцом кода, который я написал, это зависит от контекста, который для простоты игнорируется.
Марко Скаббиоло
13

У нас могут быть функции внутри функциональных компонентов без состояния, пример ниже:

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

Но это не очень хорошая практика, так как функция handlePick()будет определяться каждый раз при вызове компонента.

Ручир Саксена
источник
11
Если это не лучшая практика, то каким будет альтернативное решение?
John Samuel
@JohnSamuel Альтернативой является определение handlePick()над / вне компонента, как function handlePick() { ... }; const Action = () => { ... }результат, что handlePick определяется только один раз. Если вам нужны данные из Actionкомпонента, вы должны передать их в качестве параметров handlePickфункции.
Джеймс Хэй
12
если это не лучшая практика, вы могли бы добавить к ответу хорошую практику? теперь мне нужно снова искать хорошую практику :(
Shamseer Ahammed
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

Если вы хотите использовать реквизиты или состояние компонента в функции, это должно быть определено в компоненте с помощью 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
можете ли вы привести пример использования ловушки для метода внутри функционального компонента? (не установленный метод состояния)
Джейк-