Пользовательский веб-сокет в архитектуре Redux

Чем больше я читаю на эту тему, тем больше похоже на то, что я спускаюсь в кроличью нору. Это новое торговое приложение, которое получает данные в реальном времени через веб-сокеты, основанное на парадигме запроса-ответа. Существует три отдельных SPA, в которых, помимо начальной загрузки, каждое действие пользователя вызывает вызов хранилища данных с новым запросом MDXQuery. Так что, в свою очередь, мне нужно будет создать новые подписки на componentDidMount (), а также на соответствующие ActionCreators. Я хотел бы упростить код, чтобы избежать дублирования кода и избыточности.

Приведенный ниже код помогает установить новый канал подписки для потоковой передачи ответа через веб-сокет (в отличие от большинства кода sockets.io, где он поставляется с назначенными открытием, закрытием, отправкой)

    this.subscription = bus.channel(PATH, { mode: bus.wsModes.PULL }).createListener(this.onResponse.bind(this));
this.subscription.subscribe(MDXQuery);

Если я прочитал документацию REDUX, где мне разместить код веб-сокета? Он упоминает о создании специального промежуточного программного обеспечения. ССЫЛКА: https://redux.js.org/faq/codestructure#where-should-websockets-and-other-persistent-connections-live

Но я не очень уверен, как я могу использовать этот настраиваемый код веб-сокета, создавая мое собственное промежуточное программное обеспечение, или выполнение на уровне компонентов поможет имитировать эту стратегию.

const createMySocketMiddleware = (url) => {
return storeAPI => {
    let socket = createMyWebsocket(url);

    socket.on("message", (message) => {
        storeAPI.dispatch({
            type : "SOCKET_MESSAGE_RECEIVED",
            payload : message
        });
    });

    return next => action => {
        if (action.type == "SEND_WEBSOCKET_MESSAGE") {
            socket.send(action.payload);
            return;
        }

        return next(action);
    }
}

}

Любой дизайнерский ввод действительно поможет !!

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
2
0
847
2

Ответы 2

Я написал эту запись в FAQ и пример.

Если я понимаю ваш вопрос, вы спрашиваете, как динамически создавать дополнительные подписки во время выполнения?

Поскольку промежуточное программное обеспечение Redux может видеть каждое отправленное действие, которое проходит через конвейер промежуточного программного обеспечения, вы можете отправлять действия, которые предназначены только как команды для промежуточного программного обеспечения, которое что-то делает. Я не уверен, что такое MDXQuery, и также неясно, что вы хотите делать с сообщениями, полученными из этих подписок. В качестве примера я предполагаю, что вы хотите либо отправлять действия Redux при получении сообщения о подписке, либо потенциально выполнять с ними некоторую настраиваемую логику.

Вы можете написать собственное промежуточное ПО, которое прослушивает такие действия, как "CREATE_SUBSCRIPTION" и "CLOSE_SUBSCRIPTION", и потенциально принимает функцию обратного вызова для запуска при получении сообщения.

Вот как это может выглядеть:

// Add this to the store during setup
const subscriptionMiddleware = (storeAPI) => {
    let nextSubscriptionId = 0;
    const subscriptions = {};
    const bus = createBusSomehow();

    return (next) => (action) => {
        switch(action.type) {
            case "CREATE_SUBSCRIPTION" : {
                const {callback} = action;
                const subscriptionId = nextSubscriptionId;
                nextSubscriptionId++;

                const subscription = bus.channel(PATH, { mode: bus.wsModes.PULL })
                                        .createListener((...args) => {
                                            callback(dispatch, getState, ...args);
                                        });
                subscriptions[subscriptionId] = subscription;
                return subscriptionId;
            }
            case "CLOSE_SUBSCRIPTION" : {
                const {subscriptionId} = action;
                const subscription = subscriptions[subscriptionId];

                if (subscription) {
                    subscription.close();
                    delete subscriptions[subscriptionId];
                }
                return;
            }
        }
    }
}

// Use over in your components file
function createSubscription(callback) {
    return {type : "CREATE_SUBSCRIPTION", callback };
}

function closeSubscription(subscriptionId) {
    return {type :  "CLOSE_SUBSCRIPTION", subscriptionId};
}

// and in your component:
const actionCreators = {createSubscription, closeSubscription};

class MyComponent extends React.Component {
    componentDidMount() {
        this.subscriptionId = this.props.createSubscription(this.onMessageReceived);
    }

    componentWillUnmount() {
        this.props.closeSubscription(this.subscriptionId);
    }
}

export default connect(null, actionCreators)(MyComponent);

Привет, Марк, Во-первых, это немного для меня фанатский момент, я, должно быть, прочитал так много ваших сообщений в блоге за последние два года, которые очень помогли мне в разработанных мною приложениях React-Redux.

CodingMum 11.09.2018 10:09

Упрощенный запрос многомерных выражений был бы похож на запрос sql, но немного сложнее, используемый для аналитики больших данных. ВЫБРАТЬ НЕ ПУСТОЙ {[Меры]. [Stock_Value], [Меры]. [Stock_Prices], [Measure‌ s]. [Stock_Hike_Rate] ‌} ON COLUMNS, NON EMPTY Hierarchize (DrilldownLevel ([Stock]. [StockId]. [StockId]. [ ВСЕ]. [AllMembe‌ r])) В СТРОКАХ ОТ [StockTable] ЗНАЧЕНИЕ СВОЙСТВ ЯЧЕЙКИ '

CodingMum 11.09.2018 10:12

Да, идея состоит в том, чтобы динамически создавать подписки на веб-сокет, чтобы получать свежие данные о запасах. 1. когда компонент загружается на экран или 2. Пользователь выполняет некоторые действия на странице, выбирая некоторые новые параметры из раскрывающегося списка. Большинство экранов имеют сложные графики, созданные с использованием диаграмм или таблиц React-vis. // onResponse () - это образец обещания. Необходимо предоставить обещание для обработки ответов о подписке. let subscription = bus.channel ('/ StockMarket', {режим: bus.wsModes.PULL}). createListener (this.onResponse.bind (this));

CodingMum 11.09.2018 11:47

Ха, фанат? Приятно :) Рад, что мои посты были полезны. Помогает ли пример ответить на ваш вопрос (или, по крайней мере, дать вам представление о том, как это может работать в вашем собственном приложении), или есть аспекты, в которых вы все еще не уверены?

markerikson 11.09.2018 20:43

Я опробовал ваше решение для своей собственной проблемы, которая включает создание экземпляра сокета только тогда, когда пользователь вошел в систему, и вот как выглядит мой код:

const socketMiddleWare = url => store => {
  const socket = new SockJS(url, [], {
    sessionId: () => custom_token_id
  });
  return next => action => {
    switch (action.type) {
      case types.USER_LOGGED_IN:
        {
          socket.onopen = e => {
            console.info("Connection", e.type);

            store.dispatch({
              type: types.TOGGLE_SOCK_OPENING
            });

            if (e.type === "open") {
              store.dispatch({
                type: types.TOGGLE_SOCK_OPENED
              });
              createSession(custom_token_id, store);
              const data = {
                type: "GET_ACTIVE_SESSIONS",
                JWT_TOKEN: Cookies.get("agentClientToken")
              };
              store.dispatch({
                type: types.GET_ACTIVE_SESSIONS,
                payload: data
              });
            }
          };
          socket.onclose = () => {
            console.info("Connection closed");
            store.dispatch({
              type: types.POLL_ACTIVE_SESSIONS_STOP
            });
            // store.dispatch({ type: TOGGLE_SOCK_OPEN, payload: false });
          };
          socket.onmessage = e => {
            console.info(e)
          };
          if (
            action.type === types.SEND_SOCKET_MESSAGE
          ) {
            socket.send(JSON.stringify(action.payload));
            return;
          } else if (action.type === types.USER_LOGGED_OUT) {
            socket.close();
          }
          next(action);
        }
      default:
        next(action);
        break;
    }
  };
};

Это не работает, но не могли бы вы указать мне правильное направление? Спасибо.

Другие вопросы по теме