如何在 redux 中存储客户端?
How to store client in redux?
我正在设置一个需要创建客户端的 redux 应用程序。初始化后,客户端有侦听器和 API 需要根据某些操作调用它们。
因此,我需要保留一个客户端实例。现在,我正在将其保存在该州。是吗?
所以我有以下 redux 动作创建者,但是当我想发送消息时,我需要调用 client.say(...) API。
但是我应该从哪里得到客户端对象呢?我应该从状态中检索客户端对象吗?我的理解是这是一个 redux 反模式。使用 redux 执行此操作的正确方法是什么?
更奇怪的是——当消息发送实际上并没有改变状态时,它应该被认为是动作创建者吗?
操作:
// actions.js
import irc from 'irc';
export const CLIENT_INITIALIZE = 'CLIENT_INITIALIZE';
export const CLIENT_MESSAGE_RECEIVED = 'CLIENT_MESSAGE_RECEIVED';
export const CLIENT_MESSAGE_SEND = 'CLIENT_MESSAGE_SEND';
export function messageReceived(from, to, body) {
return {
type: CLIENT_MESSAGE_RECEIVED,
from: from,
to: to,
body: body,
};
};
export function clientSendMessage(to, body) {
client.say(...); // <--- where to get client from?
return {
type: CLIENT_MESSAGE_SEND,
to: to,
body: body,
};
};
export function clientInitialize() {
return (dispatch) => {
const client = new irc.Client('chat.freenode.net', 'react');
dispatch({
type: CLIENT_INITIALIZE,
client: client,
});
client.addListener('message', (from, to, body) => {
console.log(body);
dispatch(messageReceived(from, to, body));
});
};
};
这里是减速器:
// reducer.js
import { CLIENT_MESSAGE_RECEIVED, CLIENT_INITIALIZE } from '../actions/client';
import irc from 'irc';
export default function client(state: Object = { client: null, channels: {} }, action: Object) {
switch (action.type) {
case CLIENT_MESSAGE_RECEIVED:
return {
...state,
channels: {
...state.channels,
[action.to]: [
// an array of messages
...state.channels[action.to],
// append new message
{
to: action.to,
from: action.from,
body: action.body,
}
]
}
};
case CLIENT_JOIN_CHANNEL:
return {
...state,
channels: {
...state.channels,
[action.channel]: [],
}
};
case CLIENT_INITIALIZE:
return {
...state,
client: action.client,
};
default:
return state;
}
}
使用中间件将客户端对象注入动作创建器! :)
export default function clientMiddleware(client) {
return ({ dispatch, getState }) => {
return next => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
const { promise, ...rest } = action;
if (!promise) {
return next(action);
}
next({ ...rest });
const actionPromise = promise(client);
actionPromise.then(
result => next({ ...rest, result }),
error => next({ ...rest, error }),
).catch((error) => {
console.error('MIDDLEWARE ERROR:', error);
next({ ...rest, error });
});
return actionPromise;
};
};
}
然后应用它:
const client = new MyClient();
const store = createStore(
combineReducers({
...
}),
applyMiddleware(clientMiddleware(client))
);
然后你就可以在action creators中使用它了:
export function actionCreator() {
return {
promise: client => {
return client.doSomethingPromisey();
}
};
}
这主要改编自react-redux-universal-hot-example boilerplate project. I removed the abstraction that lets you define start, success and fail actions, which is used to create this abstraction in action creators。
如果您的客户端不是异步的,您可以调整此代码以简单地传入客户端,类似于 redux-thunk 传入 dispatch
的方式。
我正在设置一个需要创建客户端的 redux 应用程序。初始化后,客户端有侦听器和 API 需要根据某些操作调用它们。
因此,我需要保留一个客户端实例。现在,我正在将其保存在该州。是吗?
所以我有以下 redux 动作创建者,但是当我想发送消息时,我需要调用 client.say(...) API。
但是我应该从哪里得到客户端对象呢?我应该从状态中检索客户端对象吗?我的理解是这是一个 redux 反模式。使用 redux 执行此操作的正确方法是什么?
更奇怪的是——当消息发送实际上并没有改变状态时,它应该被认为是动作创建者吗?
操作:
// actions.js
import irc from 'irc';
export const CLIENT_INITIALIZE = 'CLIENT_INITIALIZE';
export const CLIENT_MESSAGE_RECEIVED = 'CLIENT_MESSAGE_RECEIVED';
export const CLIENT_MESSAGE_SEND = 'CLIENT_MESSAGE_SEND';
export function messageReceived(from, to, body) {
return {
type: CLIENT_MESSAGE_RECEIVED,
from: from,
to: to,
body: body,
};
};
export function clientSendMessage(to, body) {
client.say(...); // <--- where to get client from?
return {
type: CLIENT_MESSAGE_SEND,
to: to,
body: body,
};
};
export function clientInitialize() {
return (dispatch) => {
const client = new irc.Client('chat.freenode.net', 'react');
dispatch({
type: CLIENT_INITIALIZE,
client: client,
});
client.addListener('message', (from, to, body) => {
console.log(body);
dispatch(messageReceived(from, to, body));
});
};
};
这里是减速器:
// reducer.js
import { CLIENT_MESSAGE_RECEIVED, CLIENT_INITIALIZE } from '../actions/client';
import irc from 'irc';
export default function client(state: Object = { client: null, channels: {} }, action: Object) {
switch (action.type) {
case CLIENT_MESSAGE_RECEIVED:
return {
...state,
channels: {
...state.channels,
[action.to]: [
// an array of messages
...state.channels[action.to],
// append new message
{
to: action.to,
from: action.from,
body: action.body,
}
]
}
};
case CLIENT_JOIN_CHANNEL:
return {
...state,
channels: {
...state.channels,
[action.channel]: [],
}
};
case CLIENT_INITIALIZE:
return {
...state,
client: action.client,
};
default:
return state;
}
}
使用中间件将客户端对象注入动作创建器! :)
export default function clientMiddleware(client) {
return ({ dispatch, getState }) => {
return next => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
const { promise, ...rest } = action;
if (!promise) {
return next(action);
}
next({ ...rest });
const actionPromise = promise(client);
actionPromise.then(
result => next({ ...rest, result }),
error => next({ ...rest, error }),
).catch((error) => {
console.error('MIDDLEWARE ERROR:', error);
next({ ...rest, error });
});
return actionPromise;
};
};
}
然后应用它:
const client = new MyClient();
const store = createStore(
combineReducers({
...
}),
applyMiddleware(clientMiddleware(client))
);
然后你就可以在action creators中使用它了:
export function actionCreator() {
return {
promise: client => {
return client.doSomethingPromisey();
}
};
}
这主要改编自react-redux-universal-hot-example boilerplate project. I removed the abstraction that lets you define start, success and fail actions, which is used to create this abstraction in action creators。
如果您的客户端不是异步的,您可以调整此代码以简单地传入客户端,类似于 redux-thunk 传入 dispatch
的方式。