2017-12-11 14 views
0

非常に単純なサイクルjsサンプルアプリケーションで、ブラウザ履歴をonionify状態ストア内の状態と同期させようとしています。私は状態ストアから歴史へのマッピングに問題はなく、問題はhistory.stateを状態ストアに戻すことです。基本的に、私は、互いに流れ込む状態ストリームの無限ループに巻き込まれ、ブラウザがクラッシュします。サイクルJs - 履歴と状態を同期させる方法

import { run } from "@cycle/run"; 
import { div, button, p, makeDOMDriver } from "@cycle/dom"; 
import onionify from "cycle-onionify"; 
import { makeHistoryDriver } from "@cycle/history"; 
import xs from "xstream"; 

const main = (sources) => { 
    const popHistory$ = sources.history; // This is the problem... 

    const action$ = xs.merge(
    sources.DOM.select(".decrement").events("click").map(() => -1), 
    sources.DOM.select(".increment").events("click").map(() => +1), 
); 

    const state$ = sources.onion.state$; 

    const vdom$ = state$.map(state => 
    div([ 
     button(".decrement", "Decrement"), 
     button(".increment", "Increment"), 
     p(`Count: ${state.count}`) 
    ]) 
); 

    const initReducer$ = xs.of(function initReducer() { 
    return {count: 0}; 
    }); 

    const updateReducer$ = action$.map(x => function reducer(prevState) { 
    return {count: prevState.count + x}; 
    }); 
    // this is where the inifinity loop starts 
    const historyReducer$ = popHistory$.map(history => function(prevState) { 
    return {count: history.state.count}; 
    }); 
    // You can't merge historyReducer$ here 
    const reducer$ = xs.merge(initReducer$, updateReducer$, historyReducer$); 

    const pushHistory$ = state$.map(state => ({ 
    type: "push", 
    pathname: `/?count=${state.count}`, 
    state: state 
    })); 

    return { 
    DOM: vdom$, 
    onion: reducer$, 
    history: pushHistory$, 
    debug: popHistory$ 
    } 
}; 

const onionMain = onionify(main); 

run(onionMain, { 
    DOM: makeDOMDriver("#main"), 
    history: makeHistoryDriver(), 
    debug: $ => $.subscribe({next: console.log}) 
}); 

私は疑問に思っています。これは簡単な方法ですか?ここで役立つ演算子はありますか?私がやろうとしていることは基本的に不可能だと思う。有用なリソースへの回答やリンクは高く評価されます。

+1

のエキストラでいくつかの興味深いの演算子があります。これはストリームやCycle.jsではなく、ループ内でお互いを変えるだけのものです。だから私はより明確にする必要があります:あなたは何をフィーチャーとして達成しようとしていますか? –

+0

あなたは正しいです。それは単なる無限ループの問題でした。私は、歴史源を州と州に接続して歴史のシンクに戻す方法について混乱しました。 – MFave

+0

ヒストリシンクにヒストリソースを接続しないことでこの問題を解決し、すべてが期待通りに機能します。私の目標は、私が見つけることができないので、非常に単純なルーティングの例を作ることでした。私は自分の答えを投稿します。初めてクライアントルーティングを学習する人には便利です。 – MFave

答えて

0

私はこれを理解していることを考慮して、玉ねぎの状態ストアで履歴/状態のルーティングの最小限の作業例を投稿することもできると考えました。将来的に誰かが参考になると便利かもしれません。答えは、歴史のポップスターから歴史に戻ってくるデータを除外することでした。

import { run } from "@cycle/run"; 
import { div, button, p, makeDOMDriver } from "@cycle/dom"; 
import { makeHistoryDriver } from "@cycle/history"; 
import onionify from "cycle-onionify"; 
import xs from "xstream"; 

const main = (sources) => { 
    const { 
    DOM: domSource$, 
    history: historySource$, 
    onion: onionSource 
    } = sources; 

    const action$ = xs.merge(
    domSource$.select(".decrement").events("click") 
     .mapTo(state => ({...state, count: state.count - 1, avoidHist: false})), 

    domSource$.select(".increment").events("click") 
     .mapTo(state => ({...state, count: state.count + 1, avoidHist: false})), 

    historySource$.filter(e => e.state !== undefined) // essentially captures forward and back button events 
      .map(e => e.state.count) 
      .map(count => state => ({...state, count, avoidHist: true})), 

    historySource$.filter(e => e.state === undefined && e.hash !== "") // capture hash change events and grab state data with regex 
      .map(e => e.hash) 
      .map(hashStr => hashStr.match(/count=(-?\d{1,16})/)) 
      .filter(val => val !== null) 
      .map(arr => arr[1]) 
      .map(str => Number(str)) 
      .map(count => state => ({...state, count, avoidHist: true})) // add state property for filtering history 
); 

    const state$ = onionSource.state$; 
    const initReducer$ = xs.of(() => ({count: 0})); 
    const onionSink$ = xs.merge(initReducer$, action$); 

    const domSink$ = state$.map(state => 
    div([ 
     button(".decrement", "Decrement"), 
     button(".increment", "Increment"), 
     p(`Count: ${state.count}`) 
    ]) 
); 

    const historySink$ = state$.filter(state => state.avoidHist !== true) // filter for avoid history property 
    .map(state => ({ 
    type: "push", 
    pathname: `#count=${state.count}`, 
    state 
    })); 

    return { 
    DOM: domSink$, 
    history: historySink$, 
    onion: onionSink$ 
    }; 
}; 

const onionMain = onionify(main); 

run(onionMain, { 
    DOM: makeDOMDriver("#main"), 
    history: makeHistoryDriver() 
}); 

私は、サーバーレンダリングウェブページは、歴史との対話方法を模倣するアプリケーションの動作を作成して鍵を推測するには、履歴からのマッピングの非歴史に逆流popstateということです。今明らかになっているようだが、それを推論するまでにはしばらく時間がかかりました。

0

私が理解できるところからは、historyドライバを供給するストリームにただ一つのdropRepeatsが欠けていると思います。

これは私がテストしているコードの一部ではありませんが、私はあなたの問題を解決するのでしょう

import dropRepeats from 'xstream/extra/dropRepeats' 

// .... 

const pushHistory$ = state$.compose(dropRepeats()).map(...) 

で、このライン

const pushHistory$ = state$.map(...) 

を交換すると信じています。

これは、状態が実際には変更されていないが、状態にあるものとURLを同期させた状態でpushStateをフィルタリングします。以前の回答と同じようにすべてのイベントリスナーを変更する必要はありません。 )

コード、 `historyReducer $は`) `pushHistory $`を通じてタマネギ状態とタマネギの状態を(変更履歴の状態を変更するによるとXStreamのhttps://github.com/staltz/xstream/blob/master/EXTRA_DOCS.md

関連する問題