Search code examples
reactjsintellij-ideareduxreact-reduxwebstorm

How to quickly navigate to Redux reducer from an Action in WebStorm?


I have to write a Redux application using @reduxjs/toolkit

Here are the Redux actions in todoActions.js

// todoActions.js

import {createAction} from "@reduxjs/toolkit";

const TodoActions = {

  /**
   * @function
   * @param {String} todo
   */
    addTodo: createAction("addTodo"),
  
  /**
   * @function
   * @param {String} todoId
   */
  removeTodo: createAction("removeTodo"),
    
};

export default TodoActions;

and the reducer stays in todoReducer.js

import {createReducer} from "@reduxjs/toolkit";

import TodoActions from "../todoActions";

const initState = {
  todos: [],
};

const reducer = createReducer(initState, {
  [TodoActions.addTodo]: (state, action) => {
    state.todos = [state.todos, ...action.payload];
  },

  [TodoActions.removeTodo]: (state, action) => {
    state.todos = state.todos.filter(x=>x.id!==action.payload);
  },
  
});

export default reducer;

And here is how I use actions in a React Component

import React from 'react'
import {useDispatch} from 'redux-react-hook'
import TodoActions from './todoActions'

const AddRandomButton () {
  const dispatch = useDispatch()
  
  const clickHandler = event => {
    dispatch(TodoActions.addTodo("some random todos"))
  }
  
  return (
     <button onclick={clickHandler}>
       Add
     </button>
  )
  
}

The problem is that: it is not easy to navigate from an action to its appropriate logic. When I CTRL+CLICK on the addTodo() inside AddButton component, the IDE will jump to the definition of the addTodo(), which shows nothing about the logic of this function, because the logic is in the reducer.

Question: Is there any plugin or tips how to quickly go to the reducer instead of the action definition in IntelliJ/WebStorm?


Solution

  • I'm a Redux maintainer and creator of Redux Toolkit.

    I would strongly recommend that you use createSlice instead of createAction/createReducer directly.

    There's no reason to define the action creators separately here, and the fact that you're doing them separately is what's causing the problem you're describing.

    In addition, the object wrapping the todos array is unnecessary in this case.

    I would write this as:

    import {createSlice} from "@reduxjs/toolkit";
    
    const initialState = [];
    
    const todosSlice = createSlice({
        name: "todos",
        initialState,
        reducers: {
            addTodo(state, action) {
                state.push(action.payload);
            },
            removeTodo(state, action) {
                return state.filter(todo => todo.id !== action.payload);
            }
        }
    });
    
    export const {addTodo, removeTodo} = todosSlice.actions;
    export default todosSlice.reducer;
    

    and then pull it together at the app level like:

    import {combineReducers} from "@reduxjs/toolkit";
    import todosReducer from "features/todosSlice";
    
    export default combineReducers({
        todos: todosReducer,
    });
    

    This way, when you right-click and "Go to Definition" on addTodo, it'll jump straight to the file that has the reducer logic as well. (And, you'll have fewer files to deal with.)