ステートレスコンポーネントで機能しますか?


93

ここで<canvas>見つけたこのクールなアニメーションをReactの再利用可能なコンポーネントに変換しようとしています。このコンポーネントには、キャンバス用に1つの親コンポーネントが必要であり、。には多くの子コンポーネントが必要なようです。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 (
       ...
    );
}

それぞれの長所/短所は何ですか?それらの1つは、私のような特定のユースケースに適していますか?


既存のコードを投稿して、どのように使用されるかを確認できますか?
scimonster 2017

@Scimonster私はそれを埋め込みリンクに投稿しました、多分あなたはそれを見逃しました。リンクは次のとおり
pen

回答:


117

最初に注意することは、ステートレス機能コンポーネントはメソッドを持つことができないということです。ステートレス機能コンポーネントである場合は、呼び出しupdatedrawレンダリングを当てにするべきではありませんBall

ほとんどの場合、コンポーネント関数の外部で関数を宣言して、一度だけ宣言し、常に同じ参照を再利用する必要があります。内部で関数を宣言すると、コンポーネントがレンダリングされるたびに、関数が再度定義されます。

コンポーネント内で関数を定義して、たとえば、コンポーネントのプロパティに基づいて異なる動作をするイベントハンドラーとして割り当てる必要がある場合があります。ただし、関数を外部で定義Ballしてプロパティとバインドすることで、コードをよりクリーンにし、updateordraw関数を再利用可能にすることができます。

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

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

あなたが使用している場合はフックを、あなたは使用することができるuseCallback機能を確保するために(その依存関係の1つ場合にのみ再定義され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内の関数に関連していますBallctx親のfromを使用し、子コンポーネントのキーワード<canvas>も使用します。これらのプロパティの両方にアクセスできるように、ステートレスコンポーネントを統合して実装するための最良の方法は何でしょうか?thisBall
marksCode 2017

thisステートレス機能コンポーネントを使用する場合はありません。そのことを念頭に置いてください。キャンバスのコンテキストでは、それをすべての人に渡す必要がありますがBall、それはまったく良い音ではありません。
Marco Scabbiolo 2017

したがって、この場合、あなたが言っている子コンポーネントを持たないことが最善でしょうか?
marksCode 2017

1
@MarcoScabbioloいいえ、そうではありません。矢印関数をサポートしていないブラウザはIEだけなので、すでにかなり長い間ネイティブで矢印関数を使用しています。実際、私はこのコメントを1つの記事から見つけることができました。このコメントでは、bind特にChromeでは59より前は矢印関数よりもさらに遅いと実際に主張されています。また、Firefoxでは、どちらも同じ速度で動作するため、かなり時間がかかります。したがって、そのような場合、好ましい方法に違いはないと思います:)
VadimKalinin19年

1
@MauricioAvendañoはどちらの方法でも機能しSomethingますが、コンポーネントがコンテキストを認識するため、親コンポーネントに小道具Xがあることをコンポーネントが知ることは悪い習慣です。あなたが尋ねている質問と私が書いたサンプルコードについても同じことが起こります。それは文脈に依存しますが、簡単にするために無視されています。
MarcoScabbiolo20年

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が1回だけ定義されるという結果のhandlePick()ようにfunction handlePick() { ... }; const Action = () => { ... }、コンポーネントの上/外を定義することです。Actionコンポーネントからのデータが必要な場合は、それをパラメーターとしてhandlePick関数に渡す必要があります。
ジェームスヘイ

14
これが良い習慣ではない場合は、答えに良い習慣を追加することができますか?今、私は良い習慣をもう一度検索する必要があります:(
ShamseerAhammed19年

2

useCallback機能コンポーネントでは、以下のようにReactフックを使用できます。

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」を取得しています。これは配列です。


1

関数で小道具またはコンポーネントの状態を使用する場合は、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
機能コンポーネント内のメソッドにフックを使用する例を挙げていただけますか?(状態のメソッドを設定しない)
ジェイク・ファーガソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.