Fullstack開発者への旅- Reduxとの認証-約束ベース.


This article was originally posted on my blog at legobox



今までの週.
入札では、率直に言えば、私は自分の怠惰に関して書くつもりです.私は通常、情熱のプロジェクトを完了しない問題があるが、私は実際に私の怠惰についても書く必要がある理由を変更しようとしています.入札で私の完全な経験を得るために.時々私は実際に私はそれがおそらくそれを取るだろう問題を解決するためのより簡単な方法がある場合、私はそんなにコーディングを好きではないと思います.
今までのところ、スペクターはゆっくりとした進歩を遂げています.私にとって(ここで皮肉なことですが).今週はすべての認証関連の手続きを完了しました、そして、私は反応ベースのプロジェクトで認証を本当に見ている機会を持っていて、プロセスの若干の概要を考え出しました.

認証を構築する
実際には、認証について書かれた記事がたくさんありました.それは、プロセスに関連した多くの特異性をもたらしました.多くの時間は、いくつかのシナリオを処理するのに十分な説明がなく、他のインスタンスでは、これらのアイデアは生産準備ができていませんでした.
一方で、私たちは、それがどのように実装されるかについて説明するのによく役立つ記事を持っていましたが、サーバーによって提供されるトークン状態に関連して認証ステータスを追跡することができるようにシステムをどのように設定するのかを説明するのに失敗しました.例えば、アプリケーションの状態がこのユーザーがログインしていると言うのであれば、トークンが期限切れになり、また、ケースが反転した場合もどうなるでしょう.そして、そこには、考えるべき他のものがありました.
  • リダイレクトは、コンポーネントまたはアクションで行われますか?
  • 我々はどのように行動から非同期操作を処理しますか?
  • Reduxで働くときのベストプラクティス.
  • プロセスの終わりに、私は次の結論に達しました.
  • 約束はハンドルReduxベースの非同期アクションに入る方法です.
  • の行動は、サーバと通信して、状態を変えているだけの行動に集中することになっています.
  • 経路変更は、コンポーネントだけで、そして、他にどこにも起こらなければなりません.
  • すべてのAPIのアクションはAxiosによって動かされたAPIクラスによって管理されなければなりません.その約束以来、それはシステムの約束ベースのアーキテクチャに直接フィットします.
  • 私たちがすべてのものを非同期に取っているので、より直感的な構文のために可能であるところで、async - waitパターンを使用してください.

  • トークンと状態の関係
    状態を監視するプロセスでは、ユーザーがログインしたかどうかを判断する2つの主要なプロパティがあります.
  • サーバから受け取ったトークンは、アクセストークンとして機能することを意図していました.
  • IsAuthenticated状態プロパティの状態trueまたはfalseです.このプロパティは、トークンプロパティに依存します.
  • これらの2つを使用すると、Authチェックを行うときに監視するいくつかの組み合わせがありました.

  • IsAuthenticated = TRUE、トークンは見つかります(ユーザは実際にログインします)

  • IsAuthenticated = FALSE ,トークンが見つかった(ユーザはログインしていますが、何らかの理由で状態が台無しにされます)

  • IsAuthenticated = TRUE、トークンは期限切れになります(ユーザーはもうサーバーでログインされません、したがって、我々は対応しなければなりません)

  • IsAuthenated = falseの場合、トークンは期限切れになります(ユーザは本当にログアウトします).
  • 私は過去にこの種の構造についての記事を書きましたが、詳細について説明しましたが、この過程で私はいくつかの記事から変更しました.それはあなたがここで何が起こっているかを理解するのに役立ちます.
    その記事から3つの主要なコンポーネントハンドリングルーティングがありました.

  • AppCheckコンテナ-ユーザーがログインしているかログアウトしているかどうかを確認するルートの更新を担当します.

  • EnsureLoggedContainer -これらのルートを訪問しているユーザが認証されているか確認する

  • EnsureVisutorContainer -これらのルートを訪問しているユーザーが訪問者であることを確認してください(ログインしない).
  • これ以上のADOなしでは、これらのコンポーネントへの私の提案された変更はここにあります.
    AppCheckコンポーネントでは、状態に関連してトークンの存在を確認し、適切な状態に変更を行うために、コンポーネントのライフサイクルフックを追加しました.
    componentDidMount(){
        const { currentURL, pages, dispatch, isLoggedIn } = this.props;
    
        // note isLoggedIn is mapped directly to isAuthenticated by redux connect
    
        let token = this.props.cookies.get('uat');
        console.log(token);
        switch (isLoggedIn){
          case true: 
            if(!token){
              dispatch(actions.logout()); // this action automatically changes the state of isAuthenticated to false
            }
            break
          case false:
            if(token){
              dispatch(actions.autoLogin()); // this action automatically changes the state of isAuthenticated to true
            }
            break;
          default:
            break;
        }
      }
    
    明らかに、これは他のチェック(ensureLoggeDintainContainerとensureVisutorContainer)が同じままであることをチェックするために必要な唯一の変更です.

    reduxセットアップを約束します.
    プロジェクトでは、返されたすべてのアクションは、MaxDispatchToProps関数を使用してコンポーネントによって呼び出されるすべてのアクションを約束します.Redux Thunk Setup Promise機能からthunkミドルウェアを使用して、私はこのような動作関数を書くことができました.
    import actionType from '../action-type';
    import {api} from './'
    
    export const autoLogin = () => (dispatch, getState) => new Promise((resolve,reject)=>{
        dispatch({
            type: actionType.LOGGED_IN,
            payload: true
        })
        resolve('success');
    }).catch(err=>{
        console.log("error here", err)
        throw err
    });
    
    export const signup = (params) => (dispatch, getState) => new Promise(async (resolve, reject)=> {
        // do cool stuff then resolve/reject
        try{
            let res = await api.post('/auth/register', params)
            dispatch(autoLogin()).then(()=>{
                resolve({status:'success',message:'You successfully signed up!!!', ...res});
            }).catch(err=>{
                reject({status:'error',message:'Something went wrong!!!', ...res});
            })
        }catch(err){
            console.log(err)
            reject(err);
        }
    }).catch(err=>{
        console.log(err)
        throw err
    });
    
    Signup関数とAutoLoginの両方を見ることができますので、両方の操作を処理するために約束を使用します.このように、私たちは、それらが完了するまで、これらの行動に基づく操作を延期することができます.APIエンドポイントと対話するために、APIと呼ばれるクラスをAxios経由でAPIと相互作用するように書きました.
    import axios from 'axios'
    
    let headers = {}, running = 0;
    
    class API{
    
        SIGNUP = ''
        LOGIN =  ''
        CREATE_PROJECT = ''
    
        constructor(options = {}){
            this.axios = axios.create(options);
            this.headers = {}
        }
    
        send(type, args) {
            running++;
            const respond = (func, resp) => {
                running--;
                if (!resp) resp = {};
                func(resp.data, resp.headers, resp.status);
            };
            return new Promise(
                function(resolve, reject) {
                    this[type]
                        .apply(this, args)
                        .then(resp => {
                            respond(resolve, resp);
                        })
                        .catch(error => {
                            let resp = error.response;
                            respond(reject, resp);
                        });
                }.bind(this.axios)
            );
        }
    
        get(){
            return this.send('get', arguments)
        }
    
        post(){
            return this.send('post', arguments)
        }
    
        put(){
            return this.send('put', arguments)
        }
    
        patch(){
            return this.send('patch', arguments)
        }
    
        delete(){
            return this.send('delete', arguments)
        }
    
        getHeaders(){
            return headers
        }
    
        getHeader(name) {
            return headers[name];
        }
    
        headerIs(name, value) {
            return headers[name] == value;
        }
        setHeaders(new_headers) {
            headers = new_headers;
            return this.renew();
        }
    
        setHeader (key, value) {
            this.axios.defaults.headers.common[key] = value
            return this
        }
    
        removeHeader(name, norenew) {
            delete headers[name];
            if (!norenew) this.renew();
            return this;
        }
        renew() {
            this.axios = axios.create({
                // baseURL: process.env.API_BASE_URL,
                headers: this.headers
            });
            return this;
        }
    }
    
    export default API;
    
    Axiosの変更がアプリケーション全体で行われるので、このクラスの変更はインデックスファイルで定義された1つのインスタンスを通してプロジェクト全体を実行します
    const api = new API({
        baseURL : env.API_BASE_URL,
    })
    
    これはすべてのアクションでインポートされたインスタンスです.

    トーストと通知.
    ユーザーが何かが起こったか、何かが起こっているか、エラーが発生したならば、それを知っているようにするのは良いUIです.約束されたベースの流れでは、通常、これらのエラーをキャッチするのは、通常本当に簡単です、そして、すべての行動「それから」とキャッチフレーズは構成要素で扱われますので、我々は基本的にコンポーネントベースのトーストハンドラまたは通知器を使うことができます.
    プロジェクトでは、NPMで利用可能なTrutifyプラグインコンポーネントを使用しました.

    結論
    今週は、すべてのフロントエンドの実装をうまくいけば、私は本当にこのカウント(私のピエロの鼻を置く)を期待しています.このフェーズの後、APIは探索されます、そして、Specがバージョン管理システムとサーバーとの関係でどのように働くかについて、私はnitty grittyに入ります.