React の高度な設計と制御の紹介

不言
リリース: 2018-09-13 16:31:22
オリジナル
1288 人が閲覧しました

この記事では、React の高度な設計と制御について説明します。必要な方は参考にしていただければ幸いです。

コントロール - この概念はプログラミングにおいて非常に重要です。たとえば、制御をめぐる「ホイール」カプセル化層とビジネス消費層の間の「競争」は非常に興味深いトピックです。これは React の世界でも例外ではありません。もちろん、表面的には、「ホイール」ができるだけ多くのことを制御できることを望んでいます。 なぜなら、抽象化レイヤーが処理するロジックが増えるほど、ビジネスに電話するときに注意する必要があることが減り、より便利になるからです。使用します。ただし、「あえてやりすぎない」デザインもあります。 「車輪」と企業の主導権をめぐる綱引きは非常に興味深い。

同時に、制御機能もコンポーネントの設計と密接に関連しています。原子コンポーネントなどの原子コンポーネントの設計は非常に尊重されていますが、原子コンポーネントの概念に加えて、分子コンポーネントも存在します。分子であれ原子であれ、それらはすべてビジネス上の問題を解決するために存在する理由があります。

この記事では、React フレームワークを背景として使用して、開発中の制御に関する私の考えと結論について説明します。 Reactを使わなくても、基本的には読書に支障はありません

記事を始める前に、一冊の本を紹介したいと思います。

昨年以来、著名なテクノロジー専門家であるYan Haijingと私は共著の取り組みを開始し、今年、共同で磨き上げた書籍『React State Management and Isomorphism in Practice』がついに正式に出版されました。 !この本は、React テクノロジー スタックを中心として、React の使用法の紹介に基づいて、ソース コード レベルから Redux のアイデアを分析し、サーバーサイド レンダリングと同型アプリケーションのアーキテクチャ パターンにも焦点を当てます。この本には多くのプロジェクト例が含まれており、ユーザーに React テクノロジー スタックへの扉を開くだけでなく、読者の最先端分野に対する全体的な理解を深めます。

制御コンポーネントと非制御コンポーネントから始める

React に初めて入るとき、制御の概念について最初に触れるのは、制御コンポーネントと非制御コンポーネントです。これら 2 つの概念はフォームに関連付けられることがよくあります。ほとんどの場合、フォームや入力ボックスなどの状態制御を実装するには、制御されたコンポーネントを使用することをお勧めします。制御されたコンポーネントでは、フォームなどのデータは React コンポーネント自体によって処理されます。非制御コンポーネントは、フォームのデータが Dom 自体によって制御されることを意味します。以下は典型的な非制御コンポーネントです:

ログイン後にコピー
     

React の場合、非制御コンポーネントの状態とユーザー入力は直接制御できず、対話のために form タグのネイティブ機能にのみ依存できます。上記の例の非制御コンポーネントを制御コンポーネントに変更すると、コードも非常に単純になります:

class NameForm extends React.Component {
  state= {value: ''}

  handleChange = event => {
    this.setState({value: event.target.value});
  }

  handleSubmit = event => {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      
ログイン後にコピー
                        
    )   } }

このとき、フォームの値と動作は React コンポーネントによって制御されるため、開発がより便利になります。

これはもちろん非常に基本的な概念です。引き続きお読みください。

UI「ホイール」とControl Propsモード

上で紹介した例は、私が「狭義制御および非制御」コンポーネントと呼ぶものです。 大まかに言うと、完全に制御されていないコンポーネントとは、内部状態を含まず、props のみを受け入れる関数コンポーネントまたはステートレスコンポーネントを指すと思います。そのレンダリング動作は外部プロップによって完全に制御され、それ自体の「自律性」はありません。このようなコンポーネントは再利用性が高く、テスト容易性も優れています。

しかし、UI「ホイール」デザインでは、「半自律」または「完全に制御されていない」コンポーネントの方が良い選択となる場合があります。これを「コントロール小道具」パターンと呼びます。簡単に言うと、 コンポーネントには独自の状態があり、関連するプロパティが渡されない場合は、コンポーネントが呼び出されるときに独自の状態 statea が使用され、関連するプロパティが渡されると、コントロールがレンダリングとインタラクション ロジックを完了します。が引き渡され、その動作はビジネス消費レベルによって制御されます。

多数のコミュニティ UI「ホイール」を調査した結果、Kent C. Dodds によって作成され PayPal で使用されているコンポーネント ライブラリである downshift がこのパターンを広く採用していることがわかりました。

単に、Toogle コンポーネントを例として使用します。このコンポーネントがビジネス パーティによって呼び出された場合:

class Example extends React.Component {
  state = {on: false, inputValue: 'off'}
  handleToggle = on => {
    this.setState({on, inputValue: on ? 'on' : 'off'})
  }
  handleChange = ({target: {value}}) => {
    if (value === 'on') {
      this.setState({on: true})
    } else if (value === 'off') {
      this.setState({on: false})
    }
    this.setState({inputValue: value})
  }
  render() {
    const {on} = this.state
    return (
      <p>
        <input>
        <toggle></toggle>
      </p>
    )
  }
}
ログイン後にコピー

その効果は次のとおりです:

React の高度な設計と制御の紹介

入力ボックスを介して Toggle コンポーネントの状態切り替えを制御できます ( 「on」を入力すると状態がアクティブになり、「off」を入力すると(ステータスはグレー表示になります)、マウスをクリックして切り替えることもできます。このとき、入力ボックスの内容もそれに応じて変化します。

考えてみてください: UI コンポーネント Toggle の場合、その状態はビジネス呼び出し元によって制御できるため、使用レベルでの消費の利便性が得られます。ビジネス コードでは、入力であろうと他のコンポーネントであろうと、その状態を制御でき、呼び出し時に完全に制御できます。

同時に、Toggle コンポーネントを呼び出すときに props 値を渡さなくても、コンポーネントは引き続き正常に機能します。以下の通り:

  <toggle>
    {({on, getTogglerProps}) => (
      <p>
        <button>Toggle me</button>
        </p>
<p>{on ? 'Toggled On' : 'Toggled Off'}</p>
      
    )}
  </toggle>
ログイン後にコピー

Toggle コンポーネントは、状態を切り替えるときに内部状態を維持し、切り替え効果を実現します。同時に、レンダー プロップ モードを通じてこのコンポーネントのステータス情報を外部に出力します。

Toggle のソース コードを見てみましょう (一部のリンクは削除されています):

const callAll = (...fns) => (...args) => fns.forEach(fn => fn && fn(...args))

class Toggle extends Component {
  static defaultProps = {
    defaultOn: false,
    onToggle: () => {},
  }
  state = {
    on: this.getOn({on: this.props.defaultOn}),
  }
  getOn(state = this.state) {
    return this.isOnControlled() ? this.props.on : state.on
  }
  isOnControlled() {
    return this.props.on !== undefined
  }
  getTogglerStateAndHelpers() {
    return {
      on: this.getOn(),
      setOn: this.setOn,
      setOff: this.setOff,
      toggle: this.toggle,
    }
  }
  setOnState = (state = !this.getOn()) => {
    if (this.isOnControlled()) {
      this.props.onToggle(state, this.getTogglerStateAndHelpers())
    } else {
      this.setState({on: state}, () => {
        this.props.onToggle(
          this.getOn(),
          this.getTogglerStateAndHelpers()
        )
      })
    }
  }
  setOn = this.setOnState.bind(this, true)
  setOff = this.setOnState.bind(this, false)
  toggle = this.setOnState.bind(this, undefined)
  render() {
    const renderProp = unwrapArray(this.props.children)
    return renderProp(this.getTogglerStateAndHelpers())
  }
}

function unwrapArray(arg) {
  return Array.isArray(arg) ? arg[0] : arg
}
export default Toggle
ログイン後にコピー

关键的地方在于组件内 isOnControlled 方法判断是否有命名为 on 的属性传入:如果有,则使用 this.props.on 作为本组件状态,反之用自身 this.state.on 来管理状态。同时在 render 方法中,使用了 render prop 模式,关于这个模式本文不再探讨,感兴趣的读者可以在社区中找到很多资料,同时也可以在我新书中找到相关内容。

盘点一下,control props 模式反应了典型的控制权问题。这样的“半自治”能够完美适应业务需求,在组件设计上也更加灵活有效。

Redux 异步状态管理与控制权

提到控制权话题,怎能少得了 Redux 这样的状态管理工具。Redux 的设计在方方面面都体现出来良好的控制权处理,这里我们把注意力集中在异步状态上,更多的内容还请读者关注我的新书。

Redux 处理异步,最为人熟知的就是 Redux-thunk 这样的中间件,它由 Dan 亲自编写,并在 Redux 官方文档上被安利。它与其他所有中间件一样,将 action 到 reducer 中间的过程进行掌控,使得业务使用时可以直接 dispatch 一个函数类型的 action,实现代码也很简单:

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();

export default thunk;
ログイン後にコピー

但是很快就有人认为,这样的方案因为在中间件实现中的控制不足,导致了业务代码不够精简。我们还是需要遵循传统的 Redux 步骤:八股文似的编写 action,action creactor,reducer......于是,控制粒度更大的中间件方案应运而生

Redux-promise 中间件控制了 action type,它限制业务方在 dispatch 异步 action 时,action的 payload 属性需要是一个 Promise 对象时,执行 resolve,该中间件触发一个类型相同的 action,并将 payload 设置为 promise 的 value,并设 action.status 属性为 "success"。

export default function promiseMiddleware({ dispatch }) {
  return next => action => {
    if (!isFSA(action)) {
      return isPromise(action) ? action.then(dispatch) : next(action);
    }

    return isPromise(action.payload)
      ? action.payload
          .then(result => dispatch({ ...action, payload: result }))
          .catch(error => {
            dispatch({ ...action, payload: error, error: true });
            return Promise.reject(error);
          })
      : next(action);
  };
}
ログイン後にコピー

这样的设计与 Redux-thunk 完全不同,它将 thunk 过程控制在中间件自身中,这样一来,第三方轮子做的事情更多,因此在业务调用时更加简练方便。我们只需要正常编写 action 即可:

dispatch({
    type: GET_USER,
    payload: http.getUser(userId) // payload 为 promise 对象
})
ログイン後にコピー

我们对比一下 Redux-thunk,相对于“轮子”控制权较弱,业务方控制权更多的 Redux-thunk,实现上述三行代码,就得不得不需要:

dispatch(
    function(dispatch, getState) {
        dispatch({
            type: GET_USERE, 
            payload: userId
        })
        http.getUser(id)
            .then(response => {
                dispatch({
                    type: GET_USER_SUCCESS,
                    payload: response
                })
            })
            .catch(error => {
                dispatch({
                    type: GET_DATA_FAILED,
                    payload: error
                })
            }) 
    }
)
ログイン後にコピー

当然,Redux-promise 控制权越多,一方面带来了简练,但是另一方面,业务控制权越弱,也丧失了一定的自主性。比如如果想实现乐观更新(Optimistic updates),那就很难做了。具体详见 Issue #7

为了平衡这个矛盾,在 Redux-thunk 和 Redux-promise 这两个极端控制权理念的中间件之间,于是便存在了中间状态的中间件:Redux-promise-middleware,它与 Redux-thunk 类似,掌控粒度也类似,但是在 action 处理上更加温和和渐进,它会在适当的时机 dispatch XXX_PENDING、XXX_FULFILLED 、XXX_REJECTED 三种类型的 action,也就是说这个中间件在掌控更多逻辑的基础上,增加了和外界第三方的通信程度,不再是直接高冷地触发 XXX_FULFILLED 、XXX_REJECTED,请读者仔细体会其中不同

状态管理中的控制主义和极简主义

了解了异步状态中的控制权问题,我们再从 Redux 全局角度进行分析。在内部分享时,我将基于 Redux 封装的状态管理类库共同特性总结为这一页 slide:

React の高度な設計と制御の紹介

以上四点都是相关类库基于 Redux 所进行的简化,其中非常有意思的就是后面三点,它们无一例外地与控制权相关。以 Rematch 为代表,它不再是处理 action 到 reducer 的中间件,而是完全控制了 action creator,reducer 以及联通过程。

具体来看

  • 业务方不再需要显示申明 action type,它由类库直接函数名直接生成,如果 reducer 命名为 increment,那么 action.type 就是 increment;

  • 同时控制 reducer 和 action creator 合二为一,态管理从未变得如此简单、高效。

我把这样的实践称为控制主义或者极简主义,相比 Redux-actions 这样的状态管理类库,这样的做法更加彻底、完善。具体思想可参考 Shawn McKay 的文章,介绍的比较充分,这里我不再赘述。

概要: プログラマーと制御権

最終的に分析すると、制御権は一種の設計アイデアであり、サードパーティのクラス ライブラリとビジネス利用の間の対立と衝突です。この記事では、例として React を使用しているだけです。実際、この記事では、抽象的なカテゴリとは関係なく、制御をめぐる争いが見られます。 UI 抽象化と状態抽象化のそれぞれの制御権とコーダーの分析 密接に関係しており、プログラミング エクスペリエンスと開発効率を直接決定します。

しかし、プログラミングの初期段階では、優れた制御設計を一夜にして達成することは困難です。最前線の開発に専念し、自らのビジネスニーズを真に理解し、多数のベストプラクティスを要約し、コミュニティの本質を参照し、優れたオープンソース作品を分析することによってのみ、私たちは成長できると信じています。

関連する推奨事項:

react router4+redux でルーティング許可を制御する手順の詳細な説明

react router4+redux を使用してルーティング許可制御を実装する方法

以上がReact の高度な設計と制御の紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!