All Articles

React Native - useReducer with useContext + TypeScript

Redux has a Provider, which allows you to access the redux store from all the components. Similar to that, useContext allows you to create a Context Provider in order for you to have an access to context from all the components.

Here I am going to create UserInfoContext, which has information about a user’s name and age. And also implement a reducer to change user information.

First, you have to createContext

// UserInfoContext.tsx

import React, { createContext } from "react";

type ActionProps = {
  type: string,
  payload: {
    age: number,
  },
};

const UserInfoContext = createContext({
  userInfo: {
    username: "Jason Kang",
    age: 20,
  },
  changeUserInfo: (action: ActionProps) => {},
});

export default UserInfoContext;

Now you create a reducer to change user information

// UserInfoReducer.tsx
type ActionProps = {
  type: string,
  payload: {
    name: string
    age: number
  }
}

type IUserInfo = {
  username: string,
  age: number
}

const userInfoReducer = (state: IUserInfo, action: ActionProps) => {
  switch(action.type) {
    case 'CHANGE_USERINFO':
      const {username} = action.payload;
      return {
        ...state,
        username
      }

    case 'CHANGE_AGE':
      const {age} = action.payload;
      return {
        ...state,
        age
      }

    default:
      throw new Error(`Unhandled action type: ${action.type}`);
  }
}

And then you import this context in your App.tsx to create a context provider. And you also need to useReducer and pass it to all the components using the provider so that you can change userinfo in every single component.

// App.tsx

import React, { useReducer } from "react";
import ComponentA from "~/ComponentA";
import ComponentB from "~/ComponentB";
import ComponentC from "~/ComponentC";
import UserInfoContext from "~contexts/UserInfoContext";
import UserInfoReducer from "~reducers/UserInfoReducer";

type IUserInfo = {
  username: string;
  age: number;
};

const App = () => {
  const initialUserInfo: IUserInfo = {
    username: "Jason Kang",
    age: 20,
  };

  const [state, dispatch] = useReducer(UserInfoReducer, initialUserInfo);

  const userInfoValue = {
    initialUserInfo,
    changeUserInfo: (type: string, payload: IUserInfo) => {
      dispatch({ type, payload });
    },
  };

  return (
    <UserInfoContext.Provider value={userInfoValue}>
      <ComponentA />
      <ComponentB />
      <ComponentC />
    </UserInfoContext.Provider>
  );
};

export default App;

Now you can useContext in all the components to change user information

// ComponentA.tsx

import React, {FunctionComponent, useContext} from 'react'
import {TouchableOpacity, Text} from 'react-native

import UserInfoContext from '~contexts/UserInfoContext';


type ComponentAProps = {}

const ComponentA: FunctionComponent<ComponentAProps> = props => {
  const userInfoContext = useContext(UserInfoContext)

  const changeName = (): void => {
    userInfoContext.changeUserInfo({
      type: 'CHANGE_NAME',
      payload: {
        name: 'James Kang'
      }
    })
  }

  const changeAge = (): void => {
    userInfoContext.changeUserInfo({
      type: 'CHANGE_AGE',
      payload: {
        name: 30
      }
    })
  }

  return (
    <TouchableOpacity onPress={changeName}>
      <Text>Change Name</Text>
    </TouchableOpacity>

    <TouchableOpacity onPress={changeAge}>
      <Text>Change Age</Text>
    </TouchableOpacity>
  )
}

Now you can change username and age in every single component

Jun 15, 2020

AI Enthusiast and a Software Engineer