リストレンダリングにビューを含める強力で実績のある方法


React は非常に簡単なコードを実装することができます強力なJavaScriptライブラリです.フック、JSXの使用、簡単なコンポーネントの作成、およびより多くの機能は、開発者を数分でリッチなWeb体験を作成します.複雑さがドアをノックするとき、我々は提供される特徴とともに大きな技術的なデザインを結合しなければなりませんReact そして、我々の問題にスマートな解決をしてください.
この記事では、リストレンダリングが最も簡単なソリューションからどのように進化するかを示しますopen/close principle を使用し、visitor pattern 複雑な要件.

Consider this article to be programming language agnostic although I use React for the examples provided.


標準的な方法
項目の任意のリストをレンダリングする標準的な方法React 非常にシンプルかつ効率的です.次の例はReact 公式ドキュメント.あなたは、コードの3行または読みやすさのための8行だけの項目の任意のリストをレンダリングすることができることに気づくかもしれません.
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li>{number}</li>
);

ReactDOM.render(
  <ul>{listItems}</ul>,
  document.getElementById('root')
);
これは、項目のリストをレンダリングする主な方法であり、あなたはそれらに関与する任意のロジックや単純なロジックなしで非常に単純なリストを持つ場合には、それに従う必要があります.
いくつかの種類の視覚化があるかどうか
しかし、外部変数によってあなたのアイテムが表示されなければならない方法を変更しなければならないときに何が起こりますか?
あなたのロジックを適応させることができますし、適切なコンポーネントを作成する1つまたは任意の方法でデータをレンダリングします.たとえば、リストの代わりに前の数字をテーブルに表示する必要がある場合は、コードを変更する必要があります.この要件の他に、我々はまた、ユーザーがアイテムを表示する方法を設定できるように別のものがあります.
次のコードは、要件に到達する適切なコンポーネントを設定する前の改良点です.
const numbers = [1, 2, 3, 4, 5];

// View components
function ListView({ items }) {
  return <ul>
    {items && items.map(i => <li key={i}>{i}</li>)}
  </ul>;
}

function TableView({ items }) {
  return <table>
    <tbody>
    {items && items.map(i => <tr key={i}><td>{i}</td></tr>)}
    </tbody>
  </table>;
}

// View selector
function ViewSelector({ options, onSelect }) {
  return <div>
    {options && options.map(o => 
      <div key={o}><a href="#" onClick={() => onSelect(o)}>{o}</a></div>)
    }
  </div>;
}

// Application component
function App() {
  const options = ['list', 'table'];
  const [view, setView] = React.useState(options[0]);

  const onSelectHandler = (option) => {
    setView(option);
  };

  return <div>
    <ViewSelector options={options} onSelect={onSelectHandler} />
    {view === 'list' && <ListView items={numbers} />}
    {view === 'table' && <TableView items={numbers} />}
  </div>;
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
このコードはうまく機能し、視覚的に、本当にシンプルで読みやすいです.チームの新しい開発者として、あなたは最後のコードを最終的に理解し、各コンポーネントの責任を識別することができます.したがって、コードを展開したり、それに表示される可能性のある問題を解決することができます.
進化の例として、新しい要件は、インラインの番号を参照してくださいに追加される可能性がありますし、新しいView コンポーネントを選択するオプションに追加します.新しいコードは次のようになります.
const numbers = [1, 2, 3, 4, 5];

// Notice the new view component
function InlineView({ items }) {
  return items && items.map(i => <span>{i}</span>);
}

function ListView({ items }) {
  return <ul>
    {items && items.map(i => <li key={i}>{i}</li>)}
  </ul>;
}

function TableView({ items }) {
  return <table>
    <tbody>
    {items && items.map(i => <tr key={i}><td>{i}</td></tr>)}
    </tbody>
  </table>;
}

function ViewSelector({ options, onSelect }) {
  return <div>
    {options && options.map(o => 
      <div key={o}><a href="#" onClick={() => onSelect(o)}>{o}</a></div>)
    }
  </div>;
}

function App() {
  // Notice the new option
  const options = ['list', 'table', 'inline'];
  const [view, setView] = React.useState(options[0]);

  const onSelectHandler = (option) => {
    setView(option);
  };

  // Notice how the new component has been added depending on `view` value
  return <div>
    <ViewSelector options={options} onSelect={onSelectHandler} />
    {view === 'list' && <ListView items={numbers} />}
    {view === 'table' && <TableView items={numbers} />}
    {view === 'inline' && <InlineView items={numbers} />}
  </div>;
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
私たちはopen/close principle
今の要件は、アイテムがアプリケーションに表示される方法により多くの機能を提供することに焦点を当てていると想像してください.これに加えて、コードにより多くの品質を適用し、コードレビュープロセスでより良いライトを取得したい場合は、前のコードがopen/close principle .

The open/close principle says: "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"


我々App コンポーネントは、新しいビューが作成されるたびに変更される必要があります.テスト-ユニット、統合、または任意の他の種類-私たちがそれらをコードする必要がある場合にも同様に変更する必要があります.すべてのこれらの要因は、我々のコードがどのように働くかについてより多くの不確実性を加えます、そして、これは我々が避ける必要がある何かです.
ハウvisitor pattern 役に立つ
ここでの我々の目的はApp コードの変更を避けるコンポーネント機能.これに到達するには、以下の段落で見る変更を適用する必要があります.
まず、すべてのビュータイプを使用して新しいサービスを作成する必要がありますView 各オプションに関連するコンポーネント.
function ViewersService() {

  // service variable
  const views = {};

  return {
    // provide a copy of the views variable
    get() {
      return Object.assign({}, views);
    },

    // associate a view component to a type   
    register(type, viewComponent) {
      if(undefined === views[type]) {
        views[type] = [];
      }

      views[type].push(viewComponent);
    }
  };
}

// service instantiation
const viewers = new ViewersService();

// views registration
viewers.register('list', ListView);
viewers.register('table', TableView);
viewers.register('inline', InlineView);
第二に、我々はこのインスタンスを我々に提供しなければならないApp パラメータによるコンポーネント.そして、利用可能なオプションを取得し、ユーザーの選択に応じて適切なビューコンポーネントをレンダリングするために使用します.
次のコードでは、Validatorとして選択したオプションを使用して、Viewコンポーネントを訪問する必要があるかどうかを確認します.私たちは、この値がチェックするものであると仮定します.
// Notice viewers parameter
function App({ viewers }) {

  // Notice here that we get the views registrations from the instance
  const views = viewers.get();

  // Notice how options are obtained from the views keys
  const options = Object.keys(views);
  const [viewOption, setViewOption] = React.useState(options[0]);

  const onSelectHandler = (option) => {
    setViewOption(option);
  };

  // _views[viewOption]_ is the formula that determines the components to be visited  
  const viewsToVisit = views[viewOption];

  // Notice how we go through all views registered for the option selected and render altogether.
  const allViews = viewsToVisit.map(View => <View items={numbers} />);

  return <div>
    <ViewSelector options={options} onSelect={onSelectHandler} />
    {allViews}
  </div>;
}
一見して、このコードは、コンポーネントとオブジェクトの関係のために初心者のために少し挑戦することができます.私は、この例が比較的小さい何かであるということを知っています、しかし、より広い、より大きなアプリケーションにこの解決策を考えてください.
新しい要件の場合、開発者は新しいView コンポーネントとサービスに登録します.例として、最初の項目だけをレンダリングする必要がある場合は、次のコードを追加します.
function FirstItemView({ items }) {
  return items && <span>{items[0]}</span>;
}

// this line to be added in the proper place
viewers.register('first', FirstItemView);
ラッピング
この記事では、コードとその保全性と読みやすさをvisitor pattern 広く使用.
私は、これが非常に最初の瞬間に挑戦的であると思いますが、複雑さの増加と、したがって、コードの行が起こるとき、役に立つでしょう.
この記事の運動はどう思いますか.
これはあなたに有用であることができますまたはちょうどそれを読んで楽しいことを願っています.