Ответ
Да, Redux можно использовать без React, так как это независимая библиотека управления состоянием. В Node.js я применял Redux для:
1. Управления состоянием CLI-приложения:
const { createStore, combineReducers } = require('redux');
const reduxLogger = require('redux-logger');
// Редьюсеры
const configReducer = (state = { theme: 'dark', language: 'en' }, action) => {
switch (action.type) {
case 'SET_THEME':
return { ...state, theme: action.payload };
case 'SET_LANGUAGE':
return { ...state, language: action.payload };
default:
return state;
}
};
const tasksReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_TASK':
return [...state, {
id: Date.now(),
text: action.payload,
completed: false
}];
case 'TOGGLE_TASK':
return state.map(task =>
task.id === action.payload
? { ...task, completed: !task.completed }
: task
);
default:
return state;
}
};
// Комбинируем редьюсеры
const rootReducer = combineReducers({
config: configReducer,
tasks: tasksReducer,
});
// Создаем store с middleware
const store = createStore(
rootReducer,
applyMiddleware(reduxLogger.createLogger())
);
// Подписываемся на изменения
store.subscribe(() => {
const state = store.getState();
console.log('State changed:');
console.log('- Config:', state.config);
console.log('- Tasks:', state.tasks.length);
});
// Диспатчим действия
store.dispatch({ type: 'ADD_TASK', payload: 'Implement Redux in CLI' });
store.dispatch({ type: 'SET_THEME', payload: 'light' });
2. Обработки состояний воркеров (worker threads):
// worker-state-manager.js
const { createStore } = require('redux');
const { workerData, parentPort } = require('worker_threads');
// Редьюсер для состояния воркера
const workerReducer = (state = {
status: 'idle',
processed: 0,
errors: []
}, action) => {
switch (action.type) {
case 'PROCESS_START':
return { ...state, status: 'processing' };
case 'PROCESS_SUCCESS':
return {
...state,
status: 'idle',
processed: state.processed + 1
};
case 'PROCESS_ERROR':
return {
...state,
errors: [...state.errors, action.payload],
status: 'error'
};
case 'RESET_ERRORS':
return { ...state, errors: [], status: 'idle' };
default:
return state;
}
};
const store = createStore(workerReducer);
// Отправляем состояние в основной поток при изменениях
store.subscribe(() => {
parentPort.postMessage({
type: 'STATE_UPDATE',
payload: store.getState()
});
});
// Обработка команд из основного потока
parentPort.on('message', (action) => {
store.dispatch(action);
});
3. Управления состоянием WebSocket сервера:
// websocket-state.js
const WebSocket = require('ws');
const { createStore } = require('redux');
// Редьюсер для подключений
const connectionsReducer = (state = { clients: new Map() }, action) => {
switch (action.type) {
case 'CLIENT_CONNECTED':
state.clients.set(action.payload.clientId, {
ws: action.payload.ws,
connectedAt: Date.now(),
lastActivity: Date.now(),
});
return { ...state, clients: new Map(state.clients) };
case 'CLIENT_DISCONNECTED':
state.clients.delete(action.payload);
return { ...state, clients: new Map(state.clients) };
case 'UPDATE_ACTIVITY':
const client = state.clients.get(action.payload);
if (client) {
client.lastActivity = Date.now();
state.clients.set(action.payload, client);
}
return { ...state, clients: new Map(state.clients) };
default:
return state;
}
};
const store = createStore(connectionsReducer);
// Middleware для логирования
const logger = store => next => action => {
console.log(`[${new Date().toISOString()}] Dispatching:`, action.type);
const result = next(action);
console.log('Next state:', store.getState().clients.size, 'clients');
return result;
};
// Использование в WebSocket сервере
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
const clientId = generateId();
store.dispatch({
type: 'CLIENT_CONNECTED',
payload: { clientId, ws }
});
ws.on('message', (message) => {
store.dispatch({ type: 'UPDATE_ACTIVITY', payload: clientId });
// Обработка сообщения
});
ws.on('close', () => {
store.dispatch({ type: 'CLIENT_DISCONNECTED', payload: clientId });
});
});
// Периодическая очистка неактивных клиентов
setInterval(() => {
const state = store.getState();
const now = Date.now();
for (const [clientId, data] of state.clients) {
if (now - data.lastActivity > 300000) { // 5 минут
data.ws.terminate();
store.dispatch({ type: 'CLIENT_DISCONNECTED', payload: clientId });
}
}
}, 60000);
Преимущества Redux в Node.js:
- Предсказуемость — состояние изменяется только через actions
- Отладка — можно логировать каждое изменение состояния
- Тестируемость — редьюсеры это чистые функции
- Time-travel debugging — с redux-devtools можно воспроизводить состояния
Когда стоит использовать:
- Сложные CLI-инструменты с множеством состояний
- Серверные приложения с управляемым состоянием (игры, чаты)
- Воркеры, которым нужно синхронизировать состояние
- Тестирование бизнес-логики независимо от UI