文脈を考慮した自然な文脈管理


私は非常に多くの反応と反応のネイティブのDEVは、アプリケーションの状態を管理するためにreduxを使用して精通していることを確認しています.数ヶ月後、私はあなたがReduxの代わりに地球の状態を管理するためのコンテキストを使用する方法を書いた.それは常に可能な限り必要な場所に近い状態として状態を保つために良い練習であり、これは非常に簡単なAPIreact router . 一方で、この実践は非常に複雑なAPIReact Navigation . 反応ネイティブのような反応ネイティブのようなナビゲーションのための他の選択肢がありますが、反応ナビゲーションは反応するネイティブで最も一般的に使われたナビゲーション・ライブラリであるように見えます.そこで、ここではDEVSがネイティブの状態でコンテキストプロバイダを構成する方法を示します.
// placing all providers in the app's root

<AuthContext.provider value={authValue}>
  <ArticleContext.provider value={articleValue}>
    <UserContext.provider value={userValue}>
      <Navigator />
    </UserContext.provider>
  </ArticleContext.provider>
</AuthContext.provider>

Navigatorは、アプリケーション内のすべての他のコンポーネントにルートをナビゲーションナビゲーションコンポーネントと仮定しましょう、それはあなたのアプリケーションのパフォーマンスに負の影響を与える可能性があるので、上記のようなコンテキストプロバイダの設定を持つ可能性がありますので、プロバイダのいずれかが変更を必要としないか、この更新プログラムを使用するコンポーネントを含む変更を全体のアプリケーションを意味します.この記事では、私たちは、私たちのナビゲーションとコンテキストを設定することができます非常にきちんとした方法を示しますので、コンポーネントは、更新を必要とするプロバイダの下でのみレンダリングされます.
我々の例のアプリでは、ユーザーコンテキスト、記事のコンテキストとauthコンテキストがあります.私は最終的にどのようにコンテキストを消費することができます表示するには、記事のコンポーネントに光を当てる.

コンテキストの作成
我々は様々なコンテキストを作成することから始めます.私のプロバイダを直接使用する代わりに、私は'コントローラ'として他のコンポーネントIの用語を抽象化するのが大好きです.これにより、コンテキスト値を作成および更新するためのロジックを分離および変更することが容易になります.コントローラはプロバイダを返す
これは私たちの文脈の内容です.

import React, { useReducer, useMemo } from 'react';
import PropTypes from 'prop-types';

const initialState = {
  loggedIn: false,
  user: {}
};

const initialContext = [{ ...initialState }, () => {}];

export const AuthContext = React.createContext(initialContext);

const updater = (state, update) => {
  return { ...state, ...update };
};

export function AuthController(props) {
  const [authState, updateAuth] = useReducer(updater, initialState);
  const value = useMemo(() => [authState, updateAuth], [authState]);

  return (<AuthContext.Provider value={value}>
          {props.children}
          </AuthContext.Provider>);
}

AuthController.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]).isRequired
};

ユーザコンテキストについては、

import React, { useReducer, useMemo } from 'react';
import PropTypes from 'prop-types';

const initialState = {
  user: {}
};

const initialContext = [{ ...initialState }, () => {}];

export const UserContext = React.createContext(initialContext);

const updater = (state, update) => {
  return { ...state, ...update };
};

export function UserController(props) {
  const [userState, updateUser] = useReducer(updater, initialState);
  const value = useMemo(() => [userState, updateUser], [userState]);

  return (<UserContext.Provider value={value}>
          {props.children}
          </UserContext.Provider>);
}

UserController.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]).isRequired
};

最後に、記事のコンテキスト

import React, { useReducer, useMemo } from 'react';
import PropTypes from 'prop-types';

const initialState = {
  articles: []
};

const initialContext = [{ ...initialState }, () => {}];

export const ArticleContext = React.createContext(initialContext);

const reducer = (state, action) => {
  switch (action.type) {
    case "get":
      return {...state, articles: action.articles }
    case "add":
      return { ...state, articles: [...state.articles, action.article] };
    case "delete":
      const articles = [...state.articles];
      const filteredArticles = articles.filter(article => article.id !== action.articleId);
      return { ...state, articles:filteredArticles };
    default:
      throw new Error("Unrecognized action");
  }
};


export function ArticleController(props) {
  const [articleState, dispatch] = useReducer(reducer, initialState);
  const value = useMemo(() => [articleState, dispatch], [articleState]);

  return (<ArticleContext.Provider value={value}>
          {props.children}
          </ArticleContext.Provider>);
}

ArticleController.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]).isRequired
};

それが私たちのすべての文脈です.コンテキストプロバイダに値として2つの項目を配列で渡します.配列の最初の項目は状態です.2番目は状態を更新する関数です.この値はmemoized コンポーネントがレンダリングされるたびに新しい参照を受け取る値のために継続的な再描画を防ぐ.

ナビゲーションとコンテキストプロバイダーの分割
まず最初に、我々は我々の主なナビゲーションを作成することから始めます.あなたが反応ナビゲーションをインストールしたことを確認します
npm i react-navigation

サブナビゲータの組み合わせであるメインナビゲータを定義します.
import { createStackNavigator, createAppContainer } from 'react-navigation';
import User from './user/';
import Article from './articles/';


const Navigator = createStackNavigator(
  {
    user: {
      screen: User
    },
    article: {
      screen: Article
    }
  },
  {
    initialRouteName: 'article'
  }
);

export default createAppContainer(Navigator);

次に、ユーザープロファイルに関連するコンポーネントのサブナビゲータを作成します.
import React from 'react';
import { createStackNavigator } from 'react-navigation';
import PropTypes from 'prop-types'
import UserDetails from './user-details.js';
import EditUser from './edit-user.js';
import UserController from '../contexts/user-context.js'

const UserNavigator = createStackNavigator({
  userDetails: {
    screen: UserDetails
  },
  editUser: {
    screen: Edituser
  }
}, 
{
  initialRouteName: 'userDetails',
});

export default function User(props) {
  return (
    <UserController>
      <UserNavigator navigation={props.navigation} />
    </UserController>
  );
}

User.router = UserNavigator.router

User.propTypes = {
  navigation: PropTypes.object
};

同様に、記事関連コンポーネントのサブナビゲータ
import React from 'react';
import PropTypes from 'prop-types'
import { createStackNavigator } from 'react-navigation';
import ListArticles from './all-articles.js';
import AddArticle from './add-article.js';
import ArticlesController from '../contexts/article-context.js'

const ArticleNavigator = createStackNavigator({
  listArticles: {
    screen: ListArticles
  },
  addArticle: {
    screen: AddArticle
  }
}, 
{
  initialRouteName: 'articleDetails',
});

export default function Article(props) {
  return (
    <ArtileController>
      <ArticleNavigator navigation={props.navigation} />
    </ArticleController>
  );
}

Article.router = ArticleNavigator.router

Article.propTypes = {
  navigation: PropTypes.object
};

我々がこれまでにしたことは、それぞれのプロバイダにそれぞれを包むことができるように、我々のナビゲータを分割することです.我々のコントローラはプロバイダをレンダリングします.私たちのauthコンテキストについてはどうですか?認証は我々の全体のアプリ間で懸念することができますので、我々はすべてのコンポーネントがAuth州にアクセスできるように我々の全体のナビゲータをラップすることができます.

import React from 'react';
import Navigator from './navigator';
import { AuthController } from './context/auth-context';

export default function App() {
  return (
    <AuthController>
      <Navigator />
    </AuthController>
  );
}

我々の主なナビゲータですべての経路を置く代わりに、我々はいろいろなサブナビゲータに彼らを分解して、彼らを彼らのそれぞれのプロバイダーの子供として彼らをレンダリングして、また、彼らを主なナビゲータに輸入します.反応ネイティブのナビゲーションの詳細については、チェックアウトすることができますreact navigation docs .

消費支出
次のステップでは、コンテキストを消費します.リスト記事コンポーネントでは、ここでは、どのように記事コンテキストを消費しています.
import React, {useEffect, useContext} from 'react';
import {Text, FlatList, ScrollView, TouchableOpacity} from 'react-native';
import PropTypes from 'prop-types';
import {getArticles, removeAricleFromDatabase} from 'api';
import {ArticleContext} from './context/article-context';

export default function ListArticles (props) {
  const [articles, dispatch] = useContext(ArticleContext);

  useEffect(() => {
    getArticles()
      .then(articles => dispatch({type:'get', articles})
  }, []);


  const deleteArticle = (article) => {
  removeArticleFromDatabase(article)
    .then((data) => dispatch({type: 'delete', articleId: data.id}));


  const Item = ({id, title}) => {
   return (
     <View>
      <Text>{item.title}</Text>
      <TouchableOpacity onPress={(id) => deleteArticle(id)}>
       <Text>x</Text>
      </TouchableOpacity>
     </View>
   )
  }  

  return (
    <ScrollView>
      <FlatList
        data={articles}
        renderItem={({item}) => <Item title={item.title} id={item.id}/>}
        keyExtractor={item => item.id}
      />
      <TouchableOpacity 
       onPress={() => props.navigation.navigate('addArticle')}>
       <Text>Add new article</Text>
      </TouchableOpacity>
    </ScrollView>
  );

}

私たちはここでの反応を使用して記事のコンテキストを消費しているuseContext フック.フックのパラメータとしてコンテキストを渡し、プロバイダーで渡された値を返します.我々が我々の文脈プロバイダー価値を更新したい行動を派遣してください.プロバイダーがコンポーネントツリー階層に存在しない場合は、値を取得しません.
同様に、記事を追加するためのアクションを送ることができます.
import React, {useContext} from 'react';
import {ArticleContext} from './context/article-context';
import {saveArticleInDatabase } from 'api';

const [_, dispatch] = useContext(ArticleContext);

const addArticle = (article) => {
  saveArticleInDatabase(article)
    .then((data) => dispatch({type: 'add', article: data}));
}

/* render beautiful jsx */

我々が我々のアプリで持っている他のすべてのコンテキストは、同じコンテキストプロバイダだけで、不要な再レンダリングを防ぐためにそれを消費するコンポーネントに親だけ親を消費することができます.
ここで採用されているパターンのどれも石で投げられません.これは単に最適なコンテキストを使用して我々の反応ネイティブアプリケーションの状態を管理するためのガイドです.反応文脈についてもっと知るためにhere 何かは、公式反応docsからあります.