React-Redux i Websockets z socket.io
Jestem nowy w tej technologii React-Redux i proszę o pomoc przy implementacji.
Chcę zaimplementować jedną aplikację czatu z gniazdami (socket.io). najpierw użytkownik musi się zarejestrować (używam passport po stronie serwera), a następnie, jeśli Rejestracja się powiedzie, użytkownik musi połączyć się z webSocket.
Pomyślałem, że najlepiej będzie użyć middleware jak rury dla wszystkich działań i w zależności od tego, jakiego rodzaju działania dostaje middleware, zrobić różne rzeczy.
Jeśli typem akcji jest AUTH_USER
, Utwórz połączenie klient-serwer i skonfiguruj wszystkie zdarzenia, które będą pochodzić z serwera.
Jeśli typem akcji jest MESSAGE
Wyślij do serwera wiadomość.
Fragmenty Kodu:
----- socketMiddleware.js ----
import { AUTH_USER, MESSAGE } from '../actions/types';
import * as actions from 'actions/socket-actions';
import io from 'socket.io-client';
const socket = null;
export default function ({ dispatch }) {
return next => action => {
if(action.type == AUTH_USER) {
socket = io.connect(`${location.host}`);
socket.on('message', data => {
store.dispatch(actions.addResponse(action.data));
});
}
else if(action.type == MESSAGE && socket) {
socket.emit('user-message', action.data);
return next(action)
} else {
return next(action)
}
}
}
------ Indeks.js -------
import {createStore, applyMiddleware} from 'redux';
import socketMiddleware from './socketMiddleware';
const createStoreWithMiddleware = applyMiddleware(
socketMiddleware
)(createStore);
const store = createStoreWithMiddleware(reducer);
<Provider store={store}>
<App />
</Provider>
Co sądzisz o tej praktyce, czy jest to lepsza realizacja?
1 answers
Spoiler: obecnie rozwijam coś, co będzie aplikacją do czatu typu open-source.
Możesz to zrobić lepiej, oddzielając akcje od oprogramowania pośredniczącego, a nawet klienta gniazd od oprogramowania pośredniczącego. Dlatego, w wyniku czegoś takiego:
- typy - > typy REQUEST, SUCCESS, FAILURE dla każdego żądania (nieobowiązkowe).
- reduktor - > do przechowywania różnych stanów
- akcje - > wyślij akcje do connect / disconnect / emit / listen.
- Oprogramowanie pośredniczące -> do przetwarzania Twoich działań i przekazywania bieżącej akcji klientowi socket
- Client -> socket client (socket.io).
poniższy kod pochodzi z prawdziwej aplikacji, która jest w fazie rozwoju (czasami lekko edytowane), i są one wystarczające dla większości sytuacji, ale niektóre rzeczy, takie jak SocketClient może nie być 100% kompletna.
Działania
Chcesz, aby działania były tak proste, jak to tylko możliwe, ponieważ często są powtarzane i prawdopodobnie będziesz mieć ich wiele.
export function send(chatId, content) {
const message = { chatId, content };
return {
type: 'socket',
types: [SEND, SEND_SUCCESS, SEND_FAIL],
promise: (socket) => socket.emit('SendMessage', message),
}
}
Zauważ, że socket jest parametryzowaną funkcją, w ten sposób możemy współdzielić tę samą instancję socket w całej aplikacji i nie musimy się martwić o żaden import (pokażemy jak to zrobić później).
Middleware (socketmidd.js):
Użyjemy podobnej strategii jak erikras/react-redux-universal-hot-example , choć dla socket zamiast AJAX.
Nasze oprogramowanie pośredniczące gniazd będzie odpowiedzialne za przetwarzanie tylko żądań gniazd.
Middleware przekazuje akcję do klienta gniazda i wysyła:
- REQUEST (action
types[0]
): is requesting (action.type
jest wysyłane do reduktora). - SUCCESS (action
types[1]
): na życzenie success (action.type
i odpowiedź serwera jakoaction.result
jest wysyłana do reduktora). - FAILURE (action
types[2]
): na żądanie failure (action.type
i odpowiedź serwera jakoaction.error
są wysyłane do reduktora).
export default function socketMiddleware(socket) {
// Socket param is the client. We'll show how to set this up later.
return ({dispatch, getState}) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
/*
* Socket middleware usage.
* promise: (socket) => socket.emit('MESSAGE', 'hello world!')
* type: always 'socket'
* types: [REQUEST, SUCCESS, FAILURE]
*/
const { promise, type, types, ...rest } = action;
if (type !== 'socket' || !promise) {
// Move on! Not a socket request or a badly formed one.
return next(action);
}
const [REQUEST, SUCCESS, FAILURE] = types;
next({...rest, type: REQUEST});
return promise(socket)
.then((result) => {
return next({...rest, result, type: SUCCESS });
})
.catch((error) => {
return next({...rest, error, type: FAILURE });
})
};
}
SocketClient.js
Socket.io-client jest jedynym, który kiedykolwiek ładuje i zarządza socket. io-client.]}[nieobowiązkowe] (zob. 1 poniżej w kodzie). jedna bardzo ciekawa cecha o socket.io jest fakt, że można mieć potwierdzenia wiadomości , co byłoby typowym odpowiedzi podczas wykonywania żądania HTTP. Możemy ich użyć, aby zweryfikować, czy każde żądanie było poprawne. Należy pamiętać, że w celu skorzystania z tej funkcji serwer socket.io polecenia muszą również posiadać ten ostatni parametr potwierdzenia.
import io from 'socket.io-client';
// Example conf. You can move this to your config file.
const host = 'http://localhost:3000';
const socketPath = '/api/socket.io';
export default class socketAPI {
socket;
connect() {
this.socket = io.connect(host, { path: socketPath });
return new Promise((resolve, reject) => {
this.socket.on('connect', () => resolve());
this.socket.on('connect_error', (error) => reject(error));
});
}
disconnect() {
return new Promise((resolve) => {
this.socket.disconnect(() => {
this.socket = null;
resolve();
});
});
}
emit(event, data) {
return new Promise((resolve, reject) => {
if (!this.socket) return reject('No socket connection.');
return this.socket.emit(event, data, (response) => {
// Response is the optional callback that you can use with socket.io in every request. See 1 above.
if (response.error) {
console.error(response.error);
return reject(response.error);
}
return resolve();
});
});
}
on(event, fun) {
// No promise is needed here, but we're expecting one in the middleware.
return new Promise((resolve, reject) => {
if (!this.socket) return reject('No socket connection.');
this.socket.on(event, fun);
resolve();
});
}
}
App.js
Podczas uruchamiania naszej aplikacji inicjujemy SocketClient
i przekazujemy go do konfiguracji sklepu.
const socketClient = new SocketClient();
const store = configureStore(initialState, socketClient, apiClient);
ConfigureStore.js
Dodajemy socketMiddleware
z naszą nowo zainicjowaną SocketClient
do sklepu middlewares (pamiętasz ten parametr, który powiedzieliśmy, że wyjaśnimy później?).
export default function configureStore(initialState, socketClient, apiClient) {
const loggerMiddleware = createLogger();
const middleware = [
...
socketMiddleware(socketClient),
...
];
[nic specjalnego] stałe typów akcji
Nic specjalnego = to, co normalnie byś zrobił.
const SEND = 'redux/message/SEND';
const SEND_SUCCESS = 'redux/message/SEND_SUCCESS';
const SEND_FAIL = 'redux/message/SEND_FAIL';
[nic specjalnego] reduktor
export default function reducer(state = {}, action = {}) {
switch(action.type) {
case SEND: {
return {
...state,
isSending: true,
};
}
default: {
return state;
}
}
}
Może to wyglądać na dużo pracy, ale gdy już ją skonfigurujesz, warto. Twój odpowiedni kod będzie łatwiejszy do odczytania, debugowania i będziesz mniej podatny na błędy.
PS: ty może śledzić tę strategię z wywołaniami AJAX API, jak również.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-24 00:15:44