npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-redux-file-logger

v0.2.0

Published

This tool allows you to log Redux actions + state to files. It also provides a convenient API for file logging, so that you can add your own loggers (e.g. navigation state).

Readme

File Logger for Redux

This tool allows you to log Redux actions + state to files. It also provides a convenient API for file logging, so that you can add your own loggers (e.g. navigation state).

Use case: QA team can easily create independent logs for each issue, which makes it much easier to understand the root-cause.

Motivation

3rd party libraries like react-native-fs allows you to write data to files, but each write operation opens & closes a new output stream.

The idea of this library is to use the standard output functions and redirect the output stream to a file, so that the stream remains open. In this case the logging process doesn't affect the app performance

Installation

npm install react-native-redux-file-logger
npx pod-install

Usage

Creating Redux file logger middleware

  1. Create a configurator for createReduxFileLoggerMiddleware() that returns middleware, so that later it can be injected to the store
import type { Action, AnyAction } from 'redux';
import type { ThunkMiddleware } from 'redux-thunk';
import type { LoggerOptions } from 'react-native-redux-file-logger';
import { Platform } from 'react-native';

export async function configureReduxFileLoggerMiddleware<
    State = any,
    BasicAction extends Action = AnyAction,
>(): Promise<ThunkMiddleware<State, BasicAction, LoggerOptions<State>> | null> {
    if (process.env.NODE_ENV === `development`) {
        const {
            createReduxFileLoggerMiddleware,
            SupportedIosRootDirsEnum,
            SupportedAndroidRootDirsEnum,
        } = require('react-native-redux-file-logger');

        try {
            const rootDir =
                Platform.OS === 'android' ? SupportedAndroidRootDirsEnum.Files : SupportedIosRootDirsEnum.Cache;
            return await createReduxFileLoggerMiddleware(
                'redux-action',
                {
                    rootDir,
                    nestedDir: 'logs',
                    fileName: 'time-travel.json',
                },
                {
                    showDiff: true,
                    shouldLogPrevState: false,
                    shouldLogNextState: true,
                },
            );
        } catch (e) {
            console.error(e);
        }
    }
    return null;
}
  1. Create the store and a middleware injector. We can't just pass the middleware to configureStore(), because it's created asynchronously.
import { configureStore } from '@reduxjs/toolkit';
import { createMiddlewareInjector } from 'react-native-redux-file-logger';
import counterReducer from './features/counter/slice';

export const store = configureStore({
    reducer: { counter: counterReducer },
    middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
});

export const middlewareInjector = createMiddlewareInjector<RootState, AppDispatch>(store);

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
  1. Create Redux file logger middleware and inject it to the store.
import * as React from 'react';
import { Provider } from 'react-redux';
import { store, middlewareInjector } from './store';
import { configureReduxFileLoggerMiddleware } from 'react-native-redux-file-logger';

export default function App() {
    useEffect(() => {
        (async () => {
            const rflMiddleware = await configureReduxFileLoggerMiddleware();
            if (rflMiddleware) {
                middlewareInjector(rflMiddleware);
            }
        })();
    }, []);

  return (
    <Provider store={store}>
      ...
    </Provider>
  );
}

Creating file logger

Let's consider an example of a file logger for navigation state changes

  1. Create file logger for navigation
import { Platform } from 'react-native';
import { addFileLogger, getFileLogger, SupportedAndroidRootDirsEnum, SupportedIosRootDirsEnum } from 'react-native-redux-file-logger';

const rootDir = Platform.OS === 'android' ? SupportedAndroidRootDirsEnum.Files : SupportedIosRootDirsEnum.Cache
await addFileLogger('navigation-state', {
  rootDir,
  nestedDir: 'logs',
  fileName: 'navigation.json'
});

export const navigationStateLogger = getFileLogger(tag);
  1. Configure navigation state listener
import {createNavigationContainerRef} from '@react-navigation/native';
import {EventArg, EventListenerCallback, EventMapCore} from '@react-navigation/core';

export const navigationRef = createNavigationContainerRef();
export type StateListenerCallbackType = EventListenerCallback<EventMapCore<any>, 'state'>;
export function addNavigationStateListener(listener: StateListenerCallbackType): void {
  navigationRef.addListener('state', listener);
}
  1. Pass ref to NavigationContainer
import {navigationRef} from 'path/to/file'

return (
  <NavigationContainer ref={navigationRef} >
    ...
  </NavigationContainer>
)
  1. Use logger inside navigation state listener
import {addStateListener, StateListenerCallbackType} from 'path/to/file'
import {navigationStateLogger} from 'path/to/file'

const stateListener: StateListenerCallbackType = e => {
  if (this.isInitialized && e.data.state && e.type) {
    navigationStateLogger.log(e.data.state);
  }
};

addNavigationStateListener(stateListener);

Creating archive

Archiving logs from all file logger instances to a specified file. If you need to archive logs for a single instance, pass the tag as a second parameter (see API section).

import { Platform } from 'react-native';
import { archive, SupportedAndroidRootDirsEnum, SupportedIosRootDirsEnum } from 'react-native-redux-file-logger';

const zipFilePath = await archive({
  rootDir:
    Platform.OS === 'android'
      ? SupportedAndroidRootDirsEnum.Files
      : SupportedIosRootDirsEnum.Cache,
  fileName: 'logs.zip',
});

API

Types

LoggerOptions

type LoggerOptions<TState = any, TLogger extends {log: (message: string) => void} = Logger> = {
  actionInclusionPredicate?: InclusionPredicate<TState>;
  diffInclusionPredicate?: InclusionPredicate<TState>;

  shouldLogPrevState?: boolean;
  shouldLogNextState?: boolean;
  showDiff?: boolean;

  stateTransformer?: (state: any) => any;

  logger: TLogger;
};
  • actionInclusionPredicate - actions filtering function, called before middleware logic execution. If returns false, the middleware won't be applied
  • actionInclusionPredicate - diffs filtering function
  • shouldLogPrevState - whether to add previous state to the file
  • shouldLogNextState - whether to add next state to the file
  • showDiff - whether to add diff(prev to next) state to the file
  • stateTransformer - accepts prev & next state and applies its logic to is
  • logger - logger instance, that implements log(message: string) => void method

InclusionPredicate

type InclusionPredicate<TState> = (action: AnyAction, getState: () => TState) => boolean;

FileConfig

type FileConfig = {
  fileName: string;
  nestedDir?: string;
  rootDir: SupportedIosRootDirsEnum | SupportedAndroidRootDirsEnum | string;
}

Example:

  • rootDir: /storage/emulated/0/Android/data/com.reduxfileloggerexample/files/ (i.e. SupportedAndroidRootDirsEnum.Files)
  • nestedDir: logs
  • fileName: time-travel.json
  • Resulting path: /storage/emulated/0/Android/data/com.reduxfileloggerexample/files/logs/time-travel.json

Constants

SupportedIosRootDirsEnum

Dirs that correspond to FileManager.SearchPathDirectory in Foundation

enum SupportedIosRootDirsEnum {
  Downloads = 'Downloads',
  Documents = 'Documents',
  AppSupportFiles = 'AppSupportFiles',
  Cache = 'Cache',
}

SupportedAndroidRootDirsEnum

Dirs taken from ReactApplicationContext

enum SupportedAndroidRootDirsEnum {
  Cache = 'Cache',
  Files = 'Files',
}

Functions

createReduxFileLoggerMiddleware

async function createReduxFileLoggerMiddleware<
  State = any,
  BasicAction extends Action = AnyAction,
  ExtraThunkArg = undefined
>(
  tag: string,
  fileConfig: FileConfig,
  loggerOptions: Omit<LoggerOptions<State>, 'logger'>
): Promise<ThunkMiddleware<State, BasicAction, ExtraThunkArg>> {}

Creates a Redux file logger middleware. Notice, that it doesn't accept logger, because it's encapsulated

  • tag - unique logger identifier
  • fileConfig - determines the file path (see above)
  • loggerOptions - logger options (see above)

createLoggerMiddleware

function createLoggerMiddleware<
  State = any,
  BasicAction extends Action = AnyAction,
  ExtraThunkArg = undefined
>(options: LoggerOptions<State>): ThunkMiddleware<State, BasicAction, ExtraThunkArg> {}

Creates a logger middleware. Unlike createReduxFileLoggerMiddleware(), it accepts a logger instance, so you can provide your own implementation.

  • options - logger options (see above)

addFileLogger

const addFileLogger = async (tag: string, fileConfig: FileConfig) => Promise<void>

Creates a unique file logger instance and stores in a map. Use it when you need to add file logger in addition to Redux (e.g. navigation state change, see example above).

  • tag - unique logger identifier
  • fileConfig - determines the file path (see above)

getFileLogger

interface Logger {
  log: (message: string) => void;
}

const getFileLogger = (tag: string) => Logger | undefined

Gets a logger instance from map by tag.

  • tag - unique logger identifier

archive

async function archive(fileConfig: FileConfig, tag?: string): Promise<string> {}

Archive logs from all logger instances (or for a specific instance if tag is provided) to a file. Supports only zip format for Android. After a successful archive creation the logs are emptied.

  • tag - unique logger identifier
  • fileConfig - determines the file path (see above)

Utils

createMiddlewareInjector

function createMiddlewareInjector<S = any, D extends Dispatch = Dispatch>(store: MiddlewareAPI<D, S>) {
    return function inject(middleware: Middleware) {
        store.dispatch = middleware(store)(store.dispatch);
    };
}

Create an injector that can be used to add middlewares.

Recipes

Pulling files from Android emulators

adb root
adb pull /storage/emulated/0/Android/data/com.reduxfileloggerexample/files/example/time-travel.json /Users/{$user}/Desktop

Browsing files on iOS emulators

  1. Copy archive result to the clipboard
  2. Finder --> Go --> Go to folder
  3. Paste the value & hit enter

License

MIT

Thanks

Inspired by Oleg Titaev


Made with create-react-native-library