← Назад к вопросам

Что такое action в Redux?

1.0 Junior🔥 161 комментариев
#State Management

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое action в Redux

Action это обычный JavaScript объект, который описывает СОБЫТИЕ или НАМЕРЕНИЕ что-то изменить в состоянии приложения. Action это единственный способ отправить данные из компонента в Redux store.

Базовое определение Action

Action это объект с обязательным полем type:

// Простой action
const action = {
  type: 'INCREMENT'
};

// Action с payload (данными)
const action = {
  type: 'ADD_TODO',
  payload: 'Купить молоко'
};

// Action с несколькими полями
const action = {
  type: 'SET_USER',
  payload: {
    id: 1,
    name: 'John',
    email: 'john@example.com'
  }
};

Ключевые свойства:

  • type (обязательно) - строка, описывающая что произошло
  • payload (опционально) - данные для изменения состояния
  • Другие поля (опционально) - дополнительная информация

Action Creators - функции для создания Actions

Нормальная практика обернуть создание action в функцию:

// Простой action creator
const increment = () => ({
  type: 'INCREMENT'
});

// Action creator с параметром
const addTodo = (text) => ({
  type: 'ADD_TODO',
  payload: text
});

// Action creator с объектом
const setUser = (userId, userData) => ({
  type: 'SET_USER',
  payload: {
    userId,
    ...userData
  }
});

// Использование
dispatch(increment()); // { type: 'INCREMENT' }
dispatch(addTodo('Купить молоко')); // { type: 'ADD_TODO', payload: 'Купить молоко' }

Workflow: Action -> Dispatch -> Reducer -> State

Полный цикл Redux:

// 1. Определяем action
const incrementAction = {
  type: 'INCREMENT'
};

// 2. Отправляем action с dispatch()
store.dispatch(incrementAction);
// или через action creator
store.dispatch(increment());

// 3. Reducer обрабатывает action
function counterReducer(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

// 4. Store обновляется
// Компоненты, которые подписаны на store, перерисовываются

Пример: Todo App с Actions

// Action types - константы (лучше практика)
const ADD_TODO = 'ADD_TODO';
const REMOVE_TODO = 'REMOVE_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';

// Action creators
const addTodo = (text) => ({
  type: ADD_TODO,
  payload: {
    id: Date.now(),
    text,
    completed: false
  }
});

const removeTodo = (id) => ({
  type: REMOVE_TODO,
  payload: id
});

const toggleTodo = (id) => ({
  type: TOGGLE_TODO,
  payload: id
});

// Reducer обрабатывает actions
function todosReducer(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [...state, action.payload];
    
    case REMOVE_TODO:
      return state.filter(todo => todo.id !== action.payload);
    
    case TOGGLE_TODO:
      return state.map(todo =>
        todo.id === action.payload
          ? { ...todo, completed: !todo.completed }
          : todo
      );
    
    default:
      return state;
  }
}

// В компоненте
import { useDispatch } from 'react-redux';

function TodoApp() {
  const dispatch = useDispatch();
  const [input, setInput] = useState('');
  
  const handleAddTodo = () => {
    dispatch(addTodo(input)); // Отправляем action
    setInput('');
  };
  
  return (
    <div>
      <input value={input} onChange={e => setInput(e.target.value)} />
      <button onClick={handleAddTodo}>Add Todo</button>
    </div>
  );
}

Правила написания Actions

1. Actions должны быть синхронными (обычно)

// ❌ НЕПРАВИЛЬНО - асинхронность в action
const fetchUser = (id) => {
  return fetch(`/api/user/${id}`)
    .then(res => res.json());
};

// ✅ ПРАВИЛЬНО - асинхронность в async thunk (middleware)
import { createAsyncThunk } from '@reduxjs/toolkit';

const fetchUser = createAsyncThunk(
  'user/fetchUser',
  async (id) => {
    const response = await fetch(`/api/user/${id}`);
    return response.json();
  }
);

2. Actions должны быть serializable (легко передавать)

// ❌ НЕПРАВИЛЬНО - функции нельзя сериализировать
const action = {
  type: 'SET_HANDLER',
  payload: () => console.log('clicked')
};

// ✅ ПРАВИЛЬНО - только данные
const action = {
  type: 'SET_CALLBACK_ID',
  payload: 'onClickHandler'
};

3. Избегай мутаций

// ❌ НЕПРАВИЛЬНО - изменяет исходный объект
const updateUser = (user) => ({
  type: 'UPDATE_USER',
  payload: user // Если потом изменить user, изменится и в action
});

// ✅ ПРАВИЛЬНО - копируем данные
const updateUser = (user) => ({
  type: 'UPDATE_USER',
  payload: { ...user } // Новый объект
});

FSA - Flux Standard Action (стандарт)

Unofficial стандарт для структуры actions:

const action = {
  type: 'ADD_TODO',
  payload: { text: 'Купить молоко' },
  meta: { timestamp: Date.now() },
  error: false
};

// Для ошибок
const errorAction = {
  type: 'FETCH_USER_FAILED',
  payload: new Error('Network error'),
  meta: { userId: 1 },
  error: true
};

Redux Toolkit - современный подход

Redux Toolkit упрощает создание actions и reducers:

import { createSlice } from '@reduxjs/toolkit';

const todoSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    addTodo: (state, action) => {
      state.push({
        id: Date.now(),
        text: action.payload,
        completed: false
      });
    },
    removeTodo: (state, action) => {
      return state.filter(todo => todo.id !== action.payload);
    },
    toggleTodo: (state, action) => {
      const todo = state.find(t => t.id === action.payload);
      if (todo) {
        todo.completed = !todo.completed;
      }
    }
  }
});

// Автоматически генерируются action creators
export const { addTodo, removeTodo, toggleTodo } = todoSlice.actions;

// В компоненте
dispatch(addTodo('Купить молоко')); // Автоматически создает { type: 'todos/addTodo', payload: '...' }

Асинхронные Actions - Async Thunk

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

// Thunk - функция, которая возвращает функцию
const fetchUsers = createAsyncThunk(
  'users/fetchUsers',
  async (_, { rejectWithValue }) => {
    try {
      const response = await fetch('/api/users');
      if (!response.ok) throw new Error('Failed to fetch');
      return await response.json();
    } catch (error) {
      return rejectWithValue(error.message);
    }
  }
);

const usersSlice = createSlice({
  name: 'users',
  initialState: { data: [], loading: false, error: null },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsers.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchUsers.fulfilled, (state, action) => {
        state.data = action.payload;
        state.loading = false;
      })
      .addCase(fetchUsers.rejected, (state, action) => {
        state.error = action.payload;
        state.loading = false;
      });
  }
});

// В компоненте
const dispatch = useDispatch();
useEffect(() => {
  dispatch(fetchUsers()); // Отправляем async action
}, [dispatch]);

Итого

Action это основной строительный блок Redux:

  • Описывает что произошло в приложении
  • Отправляется через dispatch()
  • Обрабатывается reducer-ом
  • Обновляет состояние (state)
  • Компоненты перерисовываются с новыми данными

Хороший action должен быть ясным, предсказуемым и содержать ровно столько информации, сколько нужно reducer-у для обновления состояния.

Что такое action в Redux? | PrepBro