CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-reduxjs--toolkit

The official, opinionated, batteries-included toolset for efficient Redux development

Pending
Overview
Eval results
Files

react-integration.mddocs/

React Integration

Redux Toolkit provides React-specific enhancements and utilities available from @reduxjs/toolkit/react that extend the core functionality with React-optimized features.

Capabilities

Enhanced Dynamic Middleware

React-enhanced version of dynamic middleware with component-specific hook factories and context integration.

/**
 * React-enhanced dynamic middleware with hook factories
 * Available from @reduxjs/toolkit/react
 */
function createDynamicMiddleware<
  State = any,
  DispatchType extends Dispatch<AnyAction> = Dispatch<AnyAction>
>(): ReactDynamicMiddlewareInstance<State, DispatchType>;

interface ReactDynamicMiddlewareInstance<State, DispatchType> extends DynamicMiddlewareInstance<State, DispatchType> {
  /** Creates hook factory for specific React context */
  createDispatchWithMiddlewareHookFactory(context?: React.Context<any>): CreateDispatchWithMiddlewareHook<State, DispatchType>;
  
  /** Default hook factory for current React context */
  createDispatchWithMiddlewareHook: CreateDispatchWithMiddlewareHook<State, DispatchType>;
}

interface CreateDispatchWithMiddlewareHook<State, DispatchType> {
  /** Create dispatch function with additional middleware */
  (middleware: Middleware<any, State, DispatchType>[]): DispatchType;
}

Usage Examples:

import { createDynamicMiddleware } from '@reduxjs/toolkit/react';
import { Provider } from 'react-redux';

// Create React-enhanced dynamic middleware
const dynamicMiddleware = createDynamicMiddleware<RootState, AppDispatch>();

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(dynamicMiddleware.middleware)
});

// Component-specific middleware
const AnalyticsComponent = () => {
  const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook([
    analyticsMiddleware,
    performanceMiddleware
  ]);
  
  const handleAction = () => {
    // This dispatch goes through analytics and performance middleware
    dispatch(trackableAction('button_click'));
  };
  
  return <button onClick={handleAction}>Track This Click</button>;
};

// Feature-specific middleware
const FeatureComponent = () => {
  const dispatchWithFeatureMiddleware = dynamicMiddleware.createDispatchWithMiddlewareHook([
    featureLoggingMiddleware,
    featureValidationMiddleware
  ]);
  
  const handleFeatureAction = () => {
    dispatchWithFeatureMiddleware(featureAction({ data: 'test' }));
  };
  
  return <button onClick={handleFeatureAction}>Feature Action</button>;
};

// Context-specific middleware factory
const CustomContext = React.createContext<any>(null);

const ContextualComponent = () => {
  const createContextualDispatch = dynamicMiddleware.createDispatchWithMiddlewareHookFactory(CustomContext);
  const dispatch = createContextualDispatch([contextSpecificMiddleware]);
  
  return (
    <CustomContext.Provider value={/* contextual data */}>
      <button onClick={() => dispatch(contextAction())}>
        Contextual Action
      </button>
    </CustomContext.Provider>
  );
};

React Hook Patterns

Advanced patterns for integrating Redux Toolkit with React components and hooks.

/**
 * Type-safe hooks for Redux Toolkit integration
 */
interface TypedReduxHooks<RootState, AppDispatch> {
  useAppDispatch: () => AppDispatch;
  useAppSelector: TypedUseSelectorHook<RootState>;
}

/**
 * Custom hook factory for component-specific middleware
 */
type MiddlewareHookFactory<State, DispatchType> = (
  middleware: Middleware<any, State, DispatchType>[]
) => DispatchType;

Usage Examples:

import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';

// Typed hooks setup
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

// Component with typed hooks
const TypedComponent = () => {
  const dispatch = useAppDispatch(); // Fully typed dispatch
  const user = useAppSelector(state => state.auth.user); // Typed selector
  
  const handleLogin = () => {
    dispatch(loginUser({ username: 'test' })); // Type-checked action
  };
  
  return (
    <div>
      {user ? `Welcome ${user.name}` : 'Please log in'}
      <button onClick={handleLogin}>Login</button>
    </div>
  );
};

// Custom hook for feature-specific middleware
const useFeatureDispatch = () => {
  return dynamicMiddleware.createDispatchWithMiddlewareHook([
    featureMiddleware,
    debugMiddleware
  ]);
};

const FeatureComponent = () => {
  const featureDispatch = useFeatureDispatch();
  
  return (
    <button onClick={() => featureDispatch(featureAction())}>
      Feature Action with Custom Middleware
    </button>
  );
};

// Conditional middleware based on component props
const ConditionalMiddlewareComponent = ({ enableAnalytics }: { enableAnalytics: boolean }) => {
  const middleware = useMemo(() => {
    const middlewares: Middleware[] = [loggingMiddleware];
    if (enableAnalytics) {
      middlewares.push(analyticsMiddleware);
    }
    return middlewares;
  }, [enableAnalytics]);
  
  const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook(middleware);
  
  return (
    <button onClick={() => dispatch(trackedAction())}>
      {enableAnalytics ? 'Tracked Action' : 'Regular Action'}
    </button>
  );
};

Component Lifecycle Integration

Integrate Redux Toolkit with React component lifecycles for automatic cleanup and state management.

/**
 * Hook for component-scoped listeners
 */
interface ComponentListenerHook<StateType, DispatchType> {
  (options: {
    actionCreator?: any;
    matcher?: ActionMatcher<any>;
    effect: ListenerEffect<any, StateType, DispatchType, any>;
  }): void;
}

Usage Examples:

import { useEffect, useRef } from 'react';
import { createListenerMiddleware } from '@reduxjs/toolkit';

// Component-scoped listener middleware
const ComponentWithListeners = () => {
  const listenerMiddleware = useRef(createListenerMiddleware()).current;
  const dispatch = useAppDispatch();
  
  useEffect(() => {
    // Add component-specific listeners
    const unsubscribe = listenerMiddleware.startListening({
      actionCreator: componentAction,
      effect: async (action, listenerApi) => {
        // Component-specific side effects
        console.log('Component action:', action.payload);
        
        // Access component context through closure
        // Perform component-related operations
      }
    });
    
    // Cleanup on unmount
    return () => {
      unsubscribe();
      listenerMiddleware.clearListeners();
    };
  }, [listenerMiddleware]);
  
  const handleAction = () => {
    dispatch(componentAction({ data: 'component data' }));
  };
  
  return <button onClick={handleAction}>Trigger Component Action</button>;
};

// HOC for automatic middleware injection
const withMiddleware = <P extends object>(
  Component: React.ComponentType<P>,
  middleware: Middleware[]
) => {
  return (props: P) => {
    const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook(middleware);
    
    // Provide enhanced dispatch through context or props
    return <Component {...props} dispatch={dispatch} />;
  };
};

// Usage of HOC
const EnhancedComponent = withMiddleware(BaseComponent, [
  analyticsMiddleware,
  loggingMiddleware
]);

// Hook for automatic listener cleanup
const useComponentListener = (
  actionCreator: any,
  effect: (action: any) => void,
  deps: React.DependencyList = []
) => {
  const listenerRef = useRef<() => void>();
  
  useEffect(() => {
    // Remove previous listener
    listenerRef.current?.();
    
    // Add new listener
    listenerRef.current = listenerMiddleware.startListening({
      actionCreator,
      effect: (action, listenerApi) => effect(action)
    });
    
    // Cleanup function
    return () => {
      listenerRef.current?.();
    };
  }, deps);
};

// Component using automatic listeners
const AutoListenerComponent = ({ userId }: { userId: string }) => {
  useComponentListener(
    userUpdated,
    (action) => {
      if (action.payload.id === userId) {
        console.log('Current user updated:', action.payload);
      }
    },
    [userId]
  );
  
  return <div>Component with automatic listeners</div>;
};

Context-Aware Middleware

Middleware that can access React context values for enhanced functionality.

/**
 * Context-aware middleware factory
 */
interface ContextAwareMiddlewareFactory<ContextType> {
  (context: ContextType): Middleware;
}

Usage Examples:

// Theme-aware middleware
const ThemeContext = React.createContext<{ theme: 'light' | 'dark' }>({ theme: 'light' });

const createThemeAwareMiddleware = (theme: 'light' | 'dark'): Middleware => 
  (store) => (next) => (action) => {
    // Add theme information to actions
    if (action.type.startsWith('ui/')) {
      return next({
        ...action,
        meta: {
          ...action.meta,
          theme
        }
      });
    }
    return next(action);
  };

const ThemedComponent = () => {
  const { theme } = useContext(ThemeContext);
  const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook([
    createThemeAwareMiddleware(theme),
    uiLoggingMiddleware
  ]);
  
  return (
    <button onClick={() => dispatch(uiAction('button_click'))}>
      Themed Button
    </button>
  );
};

// User context-aware middleware
const UserContext = React.createContext<{ user: User | null }>({ user: null });

const createUserAwareMiddleware = (user: User | null): Middleware =>
  (store) => (next) => (action) => {
    // Add user context to all actions
    const enhancedAction = {
      ...action,
      meta: {
        ...action.meta,
        userId: user?.id,
        userRole: user?.role,
        timestamp: Date.now()
      }
    };
    
    return next(enhancedAction);
  };

const UserAwareComponent = () => {
  const { user } = useContext(UserContext);
  const dispatch = dynamicMiddleware.createDispatchWithMiddlewareHook([
    createUserAwareMiddleware(user),
    auditMiddleware
  ]);
  
  return (
    <button onClick={() => dispatch(userAction('profile_update'))}>
      Update Profile
    </button>
  );
};

// Multi-context middleware
const AppContexts = {
  theme: ThemeContext,
  user: UserContext,
  feature: FeatureContext
};

const useContextAwareDispatch = () => {
  const theme = useContext(ThemeContext);
  const user = useContext(UserContext);
  const features = useContext(FeatureContext);
  
  return dynamicMiddleware.createDispatchWithMiddlewareHook([
    createThemeAwareMiddleware(theme.theme),
    createUserAwareMiddleware(user.user),
    createFeatureAwareMiddleware(features.enabledFeatures)
  ]);
};

const MultiContextComponent = () => {
  const dispatch = useContextAwareDispatch();
  
  return (
    <button onClick={() => dispatch(contextualAction())}>
      Context-Aware Action
    </button>
  );
};

Performance Optimization Patterns

React-specific patterns for optimizing Redux Toolkit performance in React applications.

/**
 * Optimized selector patterns
 */
interface OptimizedSelectorPatterns {
  /** Memoized selector with shallow equality */
  shallowEqual: (a: any, b: any) => boolean;
  /** Selector with custom equality function */
  customEqual: (equalityFn: (a: any, b: any) => boolean) => any;
}

Usage Examples:

import { shallowEqual } from 'react-redux';
import { createSelector } from '@reduxjs/toolkit';

// Optimized selectors to prevent unnecessary re-renders
const selectOptimizedUserData = createSelector(
  [(state: RootState) => state.user.profile],
  (profile) => ({
    name: profile?.name,
    email: profile?.email,
    avatar: profile?.avatar
  })
);

const OptimizedUserComponent = () => {
  // Use shallow equality to prevent re-renders when reference changes but values don't
  const userData = useAppSelector(selectOptimizedUserData, shallowEqual);
  
  return (
    <div>
      <img src={userData.avatar} alt={userData.name} />
      <span>{userData.name}</span>
    </div>
  );
};

// Memoized component with Redux integration
const MemoizedPostItem = React.memo(({ postId }: { postId: string }) => {
  const post = useAppSelector(
    state => state.posts.entities[postId],
    shallowEqual
  );
  
  if (!post) return null;
  
  return (
    <article>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </article>
  );
});

// Virtualized list with Redux integration
const VirtualizedPostsList = () => {
  const postIds = useAppSelector(state => state.posts.ids);
  
  const renderItem = useCallback(({ index }: { index: number }) => {
    return <MemoizedPostItem key={postIds[index]} postId={postIds[index]} />;
  }, [postIds]);
  
  return (
    <VirtualList
      height={400}
      itemCount={postIds.length}
      itemSize={100}
      renderItem={renderItem}
    />
  );
};

// Debounced dispatch pattern
const useDebounceDispatch = (delay: number = 300) => {
  const dispatch = useAppDispatch();
  
  return useMemo(
    () => debounce(dispatch, delay),
    [dispatch, delay]
  );
};

const SearchInput = () => {
  const debouncedDispatch = useDebounceDispatch();
  
  const handleSearch = (query: string) => {
    debouncedDispatch(searchAction(query));
  };
  
  return (
    <input
      onChange={(e) => handleSearch(e.target.value)}
      placeholder="Search..."
    />
  );
};

// Batch updates for multiple actions
const useBatchedDispatch = () => {
  const dispatch = useAppDispatch();
  
  return useCallback((actions: AnyAction[]) => {
    unstable_batchedUpdates(() => {
      actions.forEach(action => dispatch(action));
    });
  }, [dispatch]);
};

const BatchedActionsComponent = () => {
  const batchedDispatch = useBatchedDispatch();
  
  const handleBulkUpdate = () => {
    batchedDispatch([
      updateUser({ id: '1', name: 'Alice' }),
      updateUser({ id: '2', name: 'Bob' }),
      updateUser({ id: '3', name: 'Charlie' }),
      refreshUsersList()
    ]);
  };
  
  return (
    <button onClick={handleBulkUpdate}>
      Bulk Update Users
    </button>
  );
};

Advanced Integration Patterns

Suspense Integration

// RTK Query with React Suspense
const SuspenseQueryComponent = ({ postId }: { postId: string }) => {
  const { data } = useGetPostQuery(postId, {
    // Enable Suspense mode
    suspense: true
  });
  
  // data is guaranteed to be defined in Suspense mode
  return (
    <article>
      <h1>{data.title}</h1>
      <p>{data.content}</p>
    </article>
  );
};

const AppWithSuspense = () => {
  return (
    <Suspense fallback={<div>Loading post...</div>}>
      <SuspenseQueryComponent postId="123" />
    </Suspense>
  );
};

Error Boundary Integration

// Error boundary for RTK Query errors
class RTKQueryErrorBoundary extends React.Component {
  state = { hasError: false, error: null };
  
  static getDerivedStateFromError(error: any) {
    return { hasError: true, error };
  }
  
  componentDidCatch(error: any, errorInfo: any) {
    // Log RTK Query errors
    console.error('RTK Query Error:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return <div>Something went wrong with data fetching.</div>;
    }
    
    return this.props.children;
  }
}

const AppWithErrorBoundary = () => {
  return (
    <RTKQueryErrorBoundary>
      <DataFetchingComponent />
    </RTKQueryErrorBoundary>
  );
};

Server-Side Rendering (SSR) Integration

// SSR with RTK Query
export async function getServerSideProps() {
  const store = configureStore({
    reducer: { api: api.reducer },
    middleware: (getDefaultMiddleware) =>
      getDefaultMiddleware().concat(api.middleware)
  });
  
  // Prefetch data on server
  store.dispatch(api.endpoints.getPosts.initiate());
  
  await Promise.all(api.util.getRunningOperationPromises());
  
  return {
    props: {
      initialReduxState: store.getState()
    }
  };
}

Install with Tessl CLI

npx tessl i tessl/npm-reduxjs--toolkit

docs

actions-reducers.md

async-thunks.md

core-store.md

entity-adapters.md

index.md

middleware.md

react-integration.md

rtk-query-react.md

rtk-query.md

utilities.md

tile.json