React Navigationの個人分析と融合
8300 ワード
分析React Navigation:(チュートリアルではありません)
React Nativeが公式に推奨するルーティングモジュールで、それ自体は主に3つの部分を含む.
Router
View
React Navigationモジュールは、上記の
Navigation Props
reducerがあり,展示コンポーネントがあれば,状態変化のActionをトリガし,Actionを送信する方法もあるに違いない.React Navigationは5つのActionsを与えた.
これに対応する方法は、次のとおりです.
しかし、上記の方法はいずれも補助関数であり、
融合React Navigation:
個人種目は車輪を作らずにできるだけ作らない(そんなレベルでもない).主に使用するモジュールは次のとおりです. react native redux、react-redux、redux-immutable redux-saga redux-form immutable.js reselect
immutable
まず、immutableを適用するためにルーティングのreducerを改造します.
redux-form
その後、redux−formを使用すると、
redux-saga
redux-sagaではNavigationActionsを用いて従来の状態機械思想と結びつけて,副作用状態を含むルーティング状態をsagaにカプセル化することを実現した.
redux-sagaでのルーティングアクションの使用に至るまで、ルーティングがreduxに結合する必要性を感じさせます.もちろんあなたにとっては違うかもしれませんが、コメントを残して指摘してください.
Learn once, navigate anywhere.
React Nativeが公式に推奨するルーティングモジュールで、それ自体は主に3つの部分を含む.
The Navigation Prop
Router
View
The Navigation Prop
は主にActionの配布に用いられ、この部分は後述する.まず,Router
とView
に基づいてモジュールの内蔵ナビゲーション(Navigator)を解析する.Router
Router
はReact Navigationモジュールのreducerと考えられ、具体的なルーティング動作および応答は彼女によって行われる.開発者は、Router
のカスタマイズによって、公式サイトが修正中のモジュールをブロックするルーティングの例を示すように、ルーティングの特殊な動作を実現する.ここで指摘する必要があるのはRouter
がコンポーネントの静的属性であり、高価なコンポーネントを使用する場合、hoist-non-react-staticsを使用して静的属性および方法を高次コンポーネントにコピーすることに注意し、もちろんReact Navigationが与えたWithNavigation
の方法を使用することもできる.React Navigationモジュールに内蔵されているRouter
は、次のように分けられます.StackRouter
TabRouter
View
View
は、The Navigation Prop
およびRouter
によって提供される属性によって関連コンテンツを表示するReact Navigationモジュールのディスプレイコンポーネントである.React Navigationに内蔵されているView
は、次のように分けられます.CardStack
Tabs
Drawer
React Navigationモジュールは、上記の
Router
およびView
を内蔵する配列の組み合わせに従って、3つのナビゲーション(Navigator)を外部に提供する.StackNavigator
StackRouter
CardStack
TabNavigator
TabRouter
CardStack
Tabs
DrawerNavigator
StackRouter
Drawer
Navigation Props
reducerがあり,展示コンポーネントがあれば,状態変化のActionをトリガし,Actionを送信する方法もあるに違いない.React Navigationは5つのActionsを与えた.
Navigate
Reset
Back
Set Params
Init
これに対応する方法は、次のとおりです.
navigate
setParams
goBack
しかし、上記の方法はいずれも補助関数であり、
Navigation Props
のdispatch
とstate
の属性によって生成される.dispatch
??? Actions???React Navigationモジュールは生まれつきReduxと互換性があるように見えますが、実際には、Reduxのdispatch
とstate
のルーティング部分をそれぞれNavigation Props
のdispatch
とstate
に割り当て、React Navigationが与えたaddNavigationHelpers
を使用するだけで、上記の送信Actionの生成方法を容易にすることができます.最後にReduxでルーティングを定義するreducerはルーティング状態とRedux結合を完了する.公式の例を示します.const AppNavigator = StackNavigator(AppRouteConfigs)
// reducer ,
const navReducer = (state = initialState, action) => {
const nextState = AppNavigator.router.getStateForAction(action, state)
return nextState || state
}
//
class App extends React.Component {
render() {
return (
)
}
}
const mapStateToProps = (state) => ({
nav: state.nav
})
//
const AppWithNavigationState = connect(mapStateToProps)(App);
融合React Navigation:
個人種目は車輪を作らずにできるだけ作らない(そんなレベルでもない).主に使用するモジュールは次のとおりです.
immutable
まず、immutableを適用するためにルーティングのreducerを改造します.
const navReducer = (state = initialState, action) => {
const nextState = fromJS(AppStackNavigator.router.getStateForAction(action, state.toJS()))
return nextState || state
}
redux-form
その後、redux−formを使用すると、
back
ルーティングアクションが送信されるたびに問題が発生する.フォームが破棄されるたびに、redux-formは自動的にフォームに登録され、誰がredux-formをトリガーしたのかを確認します.最終的には、Actionに制限がないため、ルーティングreducerが実行され、次のように変更されます.const initialNavState = AppStackNavigator.router.getStateForAction(
NavigationActions.init()
)
const navReducer = (state = fromJS(initialNavState), action) => {
if (
action.type === NavigationActions.NAVIGATE ||
action.type === NavigationActions.BACK ||
action.type === NavigationActions.RESET ||
action.type === NavigationActions.INIT ||
action.type === NavigationActions.SET_PARAMS ||
action.type === NavigationActions.URI
) {
console.log(action)
return fromJS(AppStackNavigator.router.getStateForAction(action, state.toJS()))
} else {
return state
}
}
export default navReducer
redux-saga
redux-sagaではNavigationActionsを用いて従来の状態機械思想と結びつけて,副作用状態を含むルーティング状態をsagaにカプセル化することを実現した.
//
const machineState = {
currentState: 'login_screen',
states: {
login_screen: {
login: 'loading'
},
loading: {
success: 'main_screen',
failure: 'error'
},
main_screen: {
logout: 'login_screen',
failure: 'error'
},
error: {
login_retry: 'login_screen',
logout_retry: 'main_screen'
}
}
}
// effects
function * clearError() {
yield delay(2000)
yield put({ type: REQUEST_ERROR, payload: '' })
}
function * mainScreenEffects() {
yield put({ type: SET_AUTH, payload: true })
yield put(NavigationActions.back())
yield put({ type: SET_LOADING, payload: { scope: 'login', loading: false } })
}
function * errorEffects(error) {
yield put({ type: REQUEST_ERROR, payload: error.message })
yield put({ type: SET_LOADING, payload: { scope: 'login', loading: false } })
yield fork(clearError)
}
function * loginEffects() {
yield put({ type: SET_AUTH, payload: false })
yield put(NavigationActions.reset({
index: 1,
actions: [
NavigationActions.navigate({ routeName: 'Main' }),
NavigationActions.navigate({ routeName: 'Login' })
]
})) // Redirect to the login page
}
const effects = {
loading: () =>
put({
type: SET_LOADING,
payload: { scope: 'login', loading: true }
}),
main_screen: () => mainScreenEffects(),
error: error => errorEffects(error),
login_screen: () => loginEffects()
}
//
const Machine = (state, effects) => {
let machineState = state
function transition(state, operation) {
const currentState = state.currentState
const nextState = state.states[currentState][operation]
? state.states[currentState][operation]
: currentState
return { ...state, currentState: nextState }
}
function operation(name) {
machineState = transition(machineState, name)
}
function getCurrentState() {
return machineState.currentState
}
const getEffect = name => (...arg) => {
operation(name)
return effects[machineState.currentState](...arg)
}
return { operation, getCurrentState, getEffect }
}
// effects
const machine = Machine(machineState, effects)
const loginEffect = machine.getEffect('login')
const failureEffect = machine.getEffect('failure')
const successEffect = machine.getEffect('success')
const logoutEffect = machine.getEffect('logout')
//
export function * loginFlow(): any {
while (true) {
const action: { type: string, payload: Immut } = yield take(LOGIN_REQUEST)
const username: string = action.payload.get('username')
const password: string = action.payload.get('password')
yield loginEffect()
try {
let isAuth: ?boolean = yield call(Api.login, { username, password })
if (isAuth) {
yield successEffect()
}
} catch (error) {
yield failureEffect(error)
machine.operation('login_retry')
}
}
}
export function * logoutFlow(): any {
while (true) {
yield take(LOGOUT_REQUEST)
try {
let isLogout: ?boolean = yield call(Api.logout)
if (isLogout) {
yield logoutEffect()
}
} catch (error) {
yield failureEffect(error)
machine.operation('logout_retry')
}
}
}
redux-sagaでのルーティングアクションの使用に至るまで、ルーティングがreduxに結合する必要性を感じさせます.もちろんあなたにとっては違うかもしれませんが、コメントを残して指摘してください.