[21/10/25]反応QnA第2話


反応器が言う素子の「合成」について説明してください.
まず、合成とは、複数の異なるオブジェクトを接続して、新しい機能やオブジェクトを構成することである.オブジェクトを継承するのではなく、有機オブジェクトにマージできます.
継承と合成

  • 継承よりも、合成されたコードを使用する変更の方が省力です.

  • 継承されたコードを使用するのは、通常、合成よりも速いです.

  • 継承と合成を検討している場合は、親(展開された)が子クラスを使用する必要がある場所で使用できる場合は、継承します.

  • 他のカテゴリのサービスだけを利用すれば、合成がいいです.
  • 個々の継承の場合、構成部品間の独立性は合成時よりも劣る可能性があります.->継承するときは、親のコンテンツを継承する観点から理解します.また,親クラスの内容を変更することは子クラスの内容にも影響するため,適切なモジュール化は実現できない.
    合成の場合,独立素子の結合であれば,継承において問題となる可能性のあるオブジェクト間の独立性は維持できると考えられる.
    反応中の合成
    リアクターは、継承ではなく合成を使用してコンポーネント間のコードを再利用することを強調します.
    反応器は、合成をほぼ2つの方法で実現し、拡張素子を生成する.
    props.children
    特殊な道具チルドレンを利用して、反応は合成を体現します.親構成部品で合成する構成部品をラップすると、親構成部品はprops.childrenで合成されます.
    function FancyBorder(props) {
      return (
        <div className={'FancyBorder FancyBorder-' + props.color}>
          {props.children}
        </div>
      );
    }
    
    function WelcomeDialog() {
      return (
        <FancyBorder color="blue">
          <h1 className="Dialog-title">
            Welcome
          </h1>
          <p className="Dialog-message">
            Thank you for visiting our spacecraft!
          </p>
        </FancyBorder>
      );
    }
    props
    複数の構成部品オブジェクトを合成する場合、slotは複数であってもよい.そうであれば、特殊なpropを使用せずに、カスタムpropertyを追加することで、合成する構成部品を渡すことができます.
    function SplitPane(props) {
      return (
        <div className="SplitPane">
          <div className="SplitPane-left">
            {props.left}
          </div>
          <div className="SplitPane-right">
            {props.right}
          </div>
        </div>
      );
    }
    
    function App() {
      return (
        <SplitPane
          left={
            <Contacts />
          }
          right={
            <Chat />
          } />
      );
    }
    整理するなら.
    反応器はpropsを用いてすべての値の伝達を実現し,階層構造を継承することなく異なる素子を有機的に合成できる.再利用と拡張のための概念を合成し,主にpropsである.childrenという特殊なPropertyとカスタムPropertyを作成し、合成する構成部品を親構成部品として渡す方法で実現します.
    継承よりも,有機性,変更は難しくなく,個体ごとに独立性を保つことができるなど,Reactは合成の概念を好むようである.
    contextを使用する必要がある場合
    素子階層が深まるにつれて,必然的に伝達支柱の役割しか果たさない素子が生じる.(props drillingと呼ばれる)ましてや、一部の素子階層では、グローバル共有が必要な(知る必要がある)状態があると、このような不要な構造が多く発生する.この問題を解決するには、react contextを使用します.
    必ず使いますか?
    contextの主な用途は、多くの重複する素子にデータを伝達することである.ただしcontextを使用すると、構成部品の再使用が難しく、必要に応じてのみ使用できます.
  • の複数のレベルの支柱にまたがる場合、contextおよび要素の合成は、この問題を解決することができる.
  • <Page user={user} avatarSize={avatarSize} />
    // Page - PageLayout
    <PageLayout user={user} avatarSize={avatarSize} />
    // Page - PageLayout - NavigationBar
    <NavigationBar user={user} avatarSize={avatarSize} />
    // ...
    <Link href={user.permalink}>
      <Avatar user={user} size={avatarSize} />
    </Link>
    上記のコードでは、dataを実際に使用しているのはAvatarコンポーネントのみである.もしこの場合propsの構造が変化したら?すべてのレベル構成部品を修正する必要があるアイテム.それ以外に、普通の面倒ではありません.
    以下のcontextはなく、Component Compositionで解決できます.
    function Page(props) {
      const user = props.user;
      const userLink = (
        <Link href={user.permalink}>
          <Avatar user={user} size={props.avatarSize} />
        </Link>
      );
      return <PageLayout userLink={userLink} />;
    }
    
    // 이제 이렇게 쓸 수 있습니다.
    <Page user={user} avatarSize={avatarSize} />
    // ... 그 아래에 ...
    <PageLayout userLink={...} />
    // ... 그 아래에 ...
    <NavigationBar userLink={...} />
    // ... 그 아래에 ...
    {props.userLink}
    これにより,中間ネストされたコンポーネントはuser,avatarSizeなどのデータを受信する必要がなくなる.
    親コンポーネントPage万user、avatarSizeなどのデータがどこで使用され、親コンポーネントの制御力がより大きくなります.
    サブコンポーネントは、レンダリング前に親コンポーネントとコミュニケーションをとり、より簡潔になると考えられます.ただし、同じデータをツリー内の複数のレベルを持つ多くの構成部品に渡す必要がある場合もあります.(単位が大きい場合)これらのデータ値が変化すると、サブアセンブリに通知されるのはcontextである.
    これらの制御の反転は、伝達を必要とするpropsの数を減少させ、トップレベルの素子の制御力をより大きくするため、通常、より簡潔なコードを記述することができる.
    しかし、このような逆転は必ずしも正しいわけではない.複雑な論理を親に移動すると、親構成部品はさらに難しくなり、子構成部品はより柔軟になる必要があります.
    整理するなら.
    props drillingが現れると、メンテナンス面-簡潔なコードを作る上で不便な点がたくさんあります.これらの問題はcontextおよびcomponent compositionで解決できるが、component compositionを用いてinversion of controlを製造することは、すべての場合において良好ではない.より多くの異なるレベルの要素がobserveの状態にある場合、contextを使用することが望ましい.
    Context APIの欠点
    再レンダリングの必要性を決定するときにリファレンスがチェックされるため、再レンダリングする必要がない場合があります.最適化はRidexと比較して行われなかったが,ビームの大きさは増加しなかったので,context API度の項目に完全に適用できた.
    問題が発生した場合は以下の通りです.(データが実際に変更されていなくても、参照値の変更によってレンダリングが再開された場合)
    class App extends React.Component {
      render() {
        return (
          // App이 다시 렌더링 될 때 마다
          // value의 객체가 새롭게 만들어지면 ref가 바뀐다.
          // 그 하위에서 이 데이터를 구독하고 있는 컴포넌트 모두가 다시 렌더링된다.
          <MyContext.Provider value={{something: 'something'}}>
            <Toolbar />
          </MyContext.Provider>
        );
      }
    }
    class App extends React.Component {
      constructor(props) {
        super(props);
        
        // 부모의 상태로 만들어버린다.
        // 상태가 변경되는 것이 아니라면, 참조가 어긋나지 않게됨
        this.state = {
          value: {something: 'something'},
        };
      }
    
      render() {
        return (
          <MyContext.Provider value={this.state.value}>
            <Toolbar />
          </MyContext.Provider>
        );
      }
    }
    破片とキーアイテム
    クリップ内でkeypropを渡すことができるのは唯一です.リストをレンダリングするときはkeypropが重要なので、まずそれを理解します.
    function Glossary(props) {
      return (
        <dl>
          {props.items.map(item => (
            // React는 `key`가 없으면 key warning을 발생합니다.
            <React.Fragment key={item.id}>
              <dt>{item.term}</dt>
              <dd>{item.description}</dd>
            </React.Fragment>
          ))}
        </dl>
      );
    }
    高次分量を知っていますか.
    高次素子とは、伝達素子によって新しい素子が生成され、返される関数です.これはreact APIの一部ではなく、reactの構成特徴からのモードである.
    通常の構成部品はpropsを受信してUIを返し、高次構成部品は新しい構成部品を返します.
    Cross Cutting Concerns横断注目点
    横断的な注目点は、システムの多くの他の部分に依存または影響を及ぼす必要があるプログラムの一部である.
    義務記録を記録するプログラムは、認証モジュール、義務記録に依存する他のデータ管理分野に影響を及ぼすか、または影響を受ける.これらの注目点を横断注目点と呼ぶ.
    横断問題を解決するために、高次素子を使用してみてください.
    以下に、外部データに依存するCommentListコンポーネントを示す.
    class CommentList extends React.Component {
      constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
        this.state = {
          // "DataSource" 는 글로벌 데이터 소스입니다.
          comments: DataSource.getComments()
        };
      }
    
      componentDidMount() {
        // 변화감지를 위해 리스너를 추가합니다.
        DataSource.addChangeListener(this.handleChange);
      }
    
      componentWillUnmount() {
        // 리스너를 제거합니다.
        DataSource.removeChangeListener(this.handleChange);
      }
    
      handleChange() {
        // 데이터 소스가 변경될때 마다 comments를 업데이트합니다.
        this.setState({
          comments: DataSource.getComments()
        });
      }
    
      render() {
        return (
          <div>
            {this.state.comments.map((comment) => (
              <Comment comment={comment} key={comment.id} />
            ))}
          </div>
        );
      }
    }
    同様に、以下に、DataSourceという外部状態に依存するBlogPostコンポーネントを示す.
    class BlogPost extends React.Component {
      constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
        this.state = {
          // 외부 상태를 구독중.
          blogPost: DataSource.getBlogPost(props.id)
        };
      }
    
      componentDidMount() {
        DataSource.addChangeListener(this.handleChange);
      }
    
      componentWillUnmount() {
        DataSource.removeChangeListener(this.handleChange);
      }
    
      handleChange() {
        this.setState({
          blogPost: DataSource.getBlogPost(this.props.id)
        });
      }
    
      render() {
        return <TextBlock text={this.state.blogPost} />;
      }
    }
    両者は同じ素子ではないが,多くの点で類似している.

  • 構成部品のインストール後にイベントリスナーを登録する

  • 外部データの変更時のステータスの更新

  • アンロード時にリスナーを削除
  • サブスクリプション・データ・ソースが大規模なアプリケーションで繰り返し表示され、setStateの同じモードが呼び出される場合は、多くのコンポーネントで論理を共有するために抽象化する必要があります.この場合,高次素子は解決できる.
    // withSubscription은 고차함수, 고차 컴포넌트이다. 로직은 오로지 이 컴포넌트 내부에서 관리된다.
    
    const CommentListWithSubscription = withSubscription(
      CommentList,
      (DataSource) => DataSource.getComments()
    );
    
    const BlogPostWithSubscription = withSubscription(
      BlogPost,
      (DataSource, props) => DataSource.getBlogPost(props.id)
    );
    // 이 함수는 컴포넌트를 매개변수로 받고..
    function withSubscription(WrappedComponent, selectData) {
      // ...다른 컴포넌트를 반환하는데...
      return class extends React.Component {
        constructor(props) {
          super(props);
          this.handleChange = this.handleChange.bind(this);
          this.state = {
            data: selectData(DataSource, props)
          };
        }
    
        componentDidMount() {
          // ... 구독을 담당하고...
          DataSource.addChangeListener(this.handleChange);
        }
    
        componentWillUnmount() {
          DataSource.removeChangeListener(this.handleChange);
        }
    
        handleChange() {
          this.setState({
            data: selectData(DataSource, this.props)
          });
        }
    
        render() {
          // ... 래핑된 컴포넌트를 새로운 데이터로 랜더링 합니다!
          // 컴포넌트에 추가로 props를 내려주는 것에 주목하세요.
          return <WrappedComponent data={this.state.data} {...this.props} />;
        }
      };
    }
    長所

  • DRY.

  • 外部データを変更するには、すべての構成部品を変更する必要はありません.

  • 抽象化(内部論理に関心を持つ必要はありません)
  • 注意事項
  • レンダリング方法では、高次構成部品は使用しないでください.一貫性が失われます.
  • render() {
      // render가 호출될 때마다 새로운 버전의 EnhancedComponent가 생성됩니다.
      // EnhancedComponent1 !== EnhancedComponent2
      const EnhancedComponent = enhance(MyComponent);
      // 때문에 매번 전체 서브트리가 마운트 해제 후 다시 마운트 됩니다!
      return <EnhancedComponent />;
    }
    高次素子使用時ref
    付着したrefは、私が望んでいる素子ではなく、高次素子に伝達され、望ましくないストリームを生成する可能性があります.forwardRefに回せばいいです.高次素子から所望の素子を受け取って付着すればよい.
    Ref
  • react docs
  • component composition