{"id":323014,"date":"2021-05-12T21:00:55","date_gmt":"2021-05-12T21:00:55","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=323014"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=323014","title":{"rendered":"React+Redoor IPC \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433"},"content":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p>\u0412 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043d\u0430\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432, \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 IPC (inter-process communication) \u043d\u0430  \u0441\u043e\u043a\u0435\u0442\u0430\u0445. \u0414\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442, \u0442\u043e\u0440\u0433\u043e\u0432\u043e\u0433\u043e \u0431\u043e\u0442\u0430, \u0433\u0434\u0435 \u0431\u044b\u043b\u0438 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u043a\u043e\u0442\u043e\u0440\u044b\u0435  \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u0438 \u0434\u0440\u0443\u0433 \u0441 \u0434\u0440\u0443\u0433\u043e\u043c.&nbsp; \u041f\u043e \u043c\u0435\u0440\u0435 \u0440\u043e\u0441\u0442\u0430 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0441\u0442\u0430\u043b \u0432\u043e\u043f\u0440\u043e\u0441 \u043e  \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0435, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445.&nbsp; \u041c\u044b \u0440\u0435\u0448\u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0432\u043e\u0435  \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f, \u043f\u043e\u0442\u043e\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0432\u0441\u0435\u0433\u043e \u0434\u0432\u0443\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u0445  <a href=\"https:\/\/github.com\/facebook\/react\" rel=\"noopener noreferrer nofollow\">react<\/a> \u0438 <a href=\"https:\/\/github.com\/rubender\/redoor\" rel=\"noopener noreferrer nofollow\">redoor<\/a>. \u042f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0432\u0430\u043c\u0438 \u043d\u0430\u0448\u0438\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c.<\/p>\n<p> \u041c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439 JSON \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438, \u0441 \u0434\u0432\u0443\u043c\u044f \u043f\u043e\u043b\u044f\u043c\u0438: \u0438\u043c\u044f \u0438 \u0434\u0430\u043d\u043d\u044b\u0435. \u0418\u043c\u044f &#8212; \u044d\u0442\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043a\u0430\u043a\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442 \u0438 \u043f\u043e\u043b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 &#8212; \u043f\u043e\u043b\u0435\u0437\u043d\u0430\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430. \u041f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"json\">{ name:'ticket_delete', data:{id:1} }<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u044b\u0440\u043e\u0439 \u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b \u043c\u0435\u043d\u044f\u043b\u0438\u0441\u044c \u043a\u0430\u0436\u0434\u0443\u044e \u043d\u0435\u0434\u0435\u043b\u044e, \u0442\u0430\u043a  \u0447\u0442\u043e \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0438 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u043c. \u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u043c\u044b\u0435 \u0435\u043c\u0443 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0442\u0430\u043a \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f, \u0443\u0434\u0430\u043b\u044f\u044f \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0430\u0431\u043e\u0440 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0432 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445. <\/p>\n<p>\u0418 \u0442\u0430\u043a \u043d\u0430\u0447\u043d\u0435\u043c.&nbsp;\u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0435\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0432\u0435\u0431 \u0441\u0435\u0440\u0432\u0435\u0440. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u0438\u0437 \u0442\u0440\u0435\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439. \u041d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435 \u043e\u043d\u0438 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043f\u0443\u043d\u043a\u0442\u0438\u0440\u043d\u044b\u043c\u0438 \u043b\u0438\u043d\u0438\u044f\u043c\u0438. \u0422\u0430\u0439\u043c\u0435\u0440, \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u0438 \u043a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u043e\u0439.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e70\/707\/eb9\/e70707eb9cf33fd4c3001aca24a86d1e.png\" width=\"579\" height=\"411\"><figcaption><\/figcaption><\/figure>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 Web Socket \u0441\u0435\u0440\u0432\u0435\u0440. <\/p>\n<pre><code class=\"javascript\">\/** src\/ws_server\/echo_server.js *\/  const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8888 });  function sendToAll( data) {   let str = JSON.stringify(data);   wss.clients.forEach(function each(client) {     client.send(str);   }); }  \/\/ \u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443 setInterval(e=&gt;{   let d = new Date();   let H = d.getHours();   let m = ('0'+d.getMinutes()).substr(-2);   let s = ('0'+d.getSeconds()).substr(-2);   let time_str = `${H}:${m}:${s}`;   sendToAll({name:'timer', data:{time_str}}); },1000);<\/code><\/pre>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 \u0434\u0430\u0442\u043e\u0439 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0441\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0438\u043c\u0441\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c. \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440:<\/p>\n<pre><code class=\"bash\">node src\/ws_server\/echo_server.js<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0414\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u0438 \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c rollup \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0438\u0436\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>rollup.config.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import serve from 'rollup-plugin-serve'; import babel from '@rollup\/plugin-babel'; import { nodeResolve } from '@rollup\/plugin-node-resolve'; import commonjs from '@rollup\/plugin-commonjs'; import hmr from 'rollup-plugin-hot' import postcss from 'rollup-plugin-postcss'; import autoprefixer from 'autoprefixer' import replace from '@rollup\/plugin-replace';  const browsers = [  \"last 2 years\",  \"&gt; 0.1%\",  \"not dead\"]  let is_production = process.env.BUILD === 'production';  const replace_cfg = {   'process.env.NODE_ENV': JSON.stringify( is_production ? 'production' : 'development' ),   preventAssignment:false, }  const babel_cfg = {     babelrc: false,     presets: [       [         \"@babel\/preset-env\",         {           targets: {             browsers: browsers           },         }       ],       \"@babel\/preset-react\"     ],     exclude: 'node_modules\/**',     plugins: [       \"@babel\/plugin-proposal-class-properties\",       [\"@babel\/plugin-transform-runtime\", {          \"regenerator\": true       }],       [ \"transform-react-jsx\" ]     ],     babelHelpers: 'runtime' }   const cfg = {   input: [     'src\/main.js',   ],   output: {     dir:'dist',     format: 'iife',     sourcemap: true,     exports: 'named',   },   inlineDynamicImports: true,   plugins: [     replace(replace_cfg),     babel(babel_cfg),     postcss({       plugins: [         autoprefixer({           overrideBrowserslist: browsers         }),       ]     }),     commonjs({         sourceMap: true,     }),     nodeResolve({         browser: true,         jsnext: true,         module: false,     }),     serve({       open: false,       host: 'localhost',       port: 3000,     }),   ], } ;   export default cfg; <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0422\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 <code>main.js<\/code> \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0435\u0433\u043e.<\/p>\n<pre><code class=\"javascript\">\/** src\/main.js *\/ import React, { createElement, Component, createContext } from 'react'; import ReactDOM from 'react-dom'; import {Connect, Provider} from '.\/store' import Timer from '.\/Timer\/Timer'  const Main = () =&gt; (   &lt;Provider&gt;     &lt;h1&gt;ws stats&lt;\/h1&gt;     &lt;Timer\/&gt;   &lt;\/Provider&gt; ); const root = document.body.appendChild(document.createElement(\"DIV\")); ReactDOM.render(&lt;Main \/&gt;, root);<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u043e\u0440 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<pre><code class=\"javascript\">\/** src\/store.js *\/ import React, { createElement, Component, createContext } from 'react'; import createStoreFactory from 'redoor'; import * as actionsWS from '.\/actionsWS' import * as actionsTimer from '.\/Timer\/actionsTimer'  const createStore = createStoreFactory({Component, createContext, createElement}); const { Provider, Connect } = createStore(   [     actionsWS,     \/\/ websocket actions     actionsTimer,  \/\/ Timer actions   ] ); export { Provider, Connect };<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u043e\u0434\u0443\u043b\u044c \u0442\u0430\u0439\u043c\u0435\u0440\u0430 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0430\u043a\u0448\u043d\u0435\u0441 \u0444\u0430\u0439\u043b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0441\u043e\u043a\u0435\u0442\u043e\u043c.<\/p>\n<pre><code class=\"javascript\">\/** src\/actionsWS.js *\/ export const  __module_name = 'actionsWS' let __emit; \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e emit \u043e\u0442 redoor export const bindStateMethods = (getState, setState, emit) =&gt; {   __emit = emit }; \/\/ \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 let wss = new WebSocket('ws:\/\/localhost:8888') \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0438\u0445 \u0432 \u043f\u043e\u0442\u043e\u043a redoor wss.onmessage = (msg) =&gt; {   let d = JSON.parse(msg.data);   __emit(d.name, d.data); } <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043d\u0430\u0434\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f \u043f\u043e\u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435. \u041d\u0430\u0448\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u043f\u043e\u043b\u044f\u043c\u0438: \u0438\u043c\u044f \u0438 \u0434\u0430\u043d\u043d\u044b\u0435. \u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 redoor \u043c\u043e\u0436\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u0442\u043e\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0438\u043c\u044f. \u0412\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u044d\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:<\/p>\n<pre><code>    +------+     | emit | --- events --+--------------+----- ... ------+-------------&gt; +------+              |              |                |                       v              v                v                  +----------+   +----------+     +----------+                  | actions1 |   | actions2 | ... | actionsN |                  +----------+   +----------+     +----------+<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0438\u043c\u0435\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c &#171;\u0441\u043b\u0443\u0448\u0430\u0442\u044c&#187; \u0441\u0432\u043e\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438 \u043f\u043e \u043d\u0430\u0434\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u0438 \u0447\u0443\u0436\u0438\u0435 \u0442\u043e\u0436\u0435. <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441\u0430\u043c \u043c\u043e\u0434\u0443\u043b\u044c \u0442\u0430\u0439\u043c\u0435\u0440\u0430. \u0412 \u043f\u0430\u043f\u043a\u0435 <code>Timer<\/code> \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u0432\u0430 \u0444\u0430\u0439\u043b\u0430 <code>Timer.js<\/code> \u0438 <code>actionsTimer.js<\/code><\/p>\n<pre><code class=\"javascript\">\/** src\/Timer\/Timer.js *\/  import React from 'react'; import {Connect} from '..\/store' import s from '.\/Timer.module.css'  const Timer = ({timer_str}) =&gt; &lt;div className={s.root}&gt;   {timer_str} &lt;\/div&gt;  export default Connect(Timer);<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0435 \u043f\u0440\u043e\u0441\u0442\u043e, \u0442\u0430\u0439\u043c\u0435\u0440 \u0431\u0435\u0440\u0435\u0442 \u0438\u0437 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0442\u0435\u0439\u0442\u0430 <code>timer_str<\/code> \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 <code>actionsTimer.js<\/code>. \u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>Connect<\/code> \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043c\u043e\u0434\u0443\u043b\u044c \u043a redoor.<\/p>\n<pre><code class=\"javascript\">\/** src\/Timer\/actionsTimer.js *\/ export const  __module_name = 'actionsTimer' let __setState;  \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0435\u0439\u0442\u0430 export const bindStateMethods = (getState, setState) =&gt; {   __setState = setState; };  \/\/ \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0442\u0430\u0439\u043c\u0435\u0440\u0430 export const initState = {   timer_str:'' }  \/\/ \"\u0441\u043b\u0443\u0448\u0430\u0435\u043c\" \u043f\u043e\u0442\u043e\u043a \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \"timer\" export const listen = (name,data) =&gt;{   name === 'timer' &amp;&amp; updateTimer(data); } \/\/ \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0435\u0439\u0442  function updateTimer(data) {   __setState({timer_str:data.time_str}) }<\/code><\/pre>\n<p>\u0412 \u0430\u043a\u0448\u0435\u0441 \u0444\u0430\u0439\u043b\u0435, \u043c\u044b &#171;\u0441\u043b\u0443\u0448\u0430\u0435\u043c&#187; \u0441\u043e\u0431\u044b\u0442\u0438\u0435 <code>timer<\/code> \u0442\u0430\u0439\u043c\u0435\u0440\u0430 (\u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>listen<\/code>) \u0438 \u043a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0435\u0439\u0442 \u0438 \u0432\u044b\u0432\u043e\u0434\u0438\u043c \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438.  <\/p>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0445 redoor:<\/p>\n<p> <code>__module_name<\/code> &#8212; \u0437\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043d\u0443\u0436\u043d\u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u043e\u043d\u0430 \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442 \u0432 \u043a\u0430\u043a\u043e\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0432\u0445\u043e\u0434\u044f\u0442 \u0430\u043a\u0448\u0435\u043d\u0441\u044b. <\/p>\n<p><code>bindStateMethods<\/code> &#8212; \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f <code>setState<\/code>, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0435\u0439\u0442\u0430.<\/p>\n<p><code>initState<\/code> &#8212; \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0438\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u044f \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e <code>timer_str<\/code><\/p>\n<p><code>listen<\/code>&#8212; \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u0432\u0441\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 redoor. <\/p>\n<p>\u0413\u043e\u0442\u043e\u0432\u043e. \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u044e \u0438 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <code>http:\/\/localhost:3000<\/code><\/p>\n<pre><code>npx rollup -c rollup.config.js --watch<\/code><\/pre>\n<p>\u0414\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0447\u0430\u0441\u0438\u043a\u0438 \u0441 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c. \u041f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u043e\u043c\u0443. \u041f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441 \u0442\u0430\u0439\u043c\u0435\u0440\u043e\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0449\u0435 \u043c\u043e\u0434\u0443\u043b\u044c \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 <code>echo_server.js<\/code><\/p>\n<pre><code class=\"javascript\">\/** src\/ws_server\/echo_server.js *\/  ... let g_interval = 1; \/\/ \u0414\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 setInterval(e=&gt;{   let stats_array = [];   for(let i=0;i&lt;30;i++) {     stats_array.push((Math.random()*(i*g_interval))|0);   }   let data  = {     stats_array   }   sendToAll({name:'stats', data}); },500);  ... <\/code><\/pre>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c \u0432 \u043f\u0440\u043e\u0435\u043a\u0442. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443 <code>Stats<\/code> \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c <code>Stats.js<\/code> \u0438 <code>actionsStats.js<\/code><\/p>\n<pre><code class=\"javascript\">\/** src\/Stats\/Stats.js *\/ import React from 'react'; import {Connect} from '..\/store' import s from '.\/Stats.module.css'  const Bar = ({h})=&gt;&lt;div className={s.bar} style={{height:`${h}`px}}&gt;   {h} &lt;\/div&gt;  const Stats = ({stats_array})=&gt;&lt;div className={s.root}&gt;   &lt;div className={s.bars}&gt;     {stats_array.map((it,v)=&gt;&lt;Bar key={v} h={it} \/&gt;)}   &lt;\/div&gt; &lt;\/div&gt;  export default Connect(Stats);<\/code><\/pre>\n<pre><code class=\"javascript\">\/** src\/Stats\/actionsStats.js *\/ export const  __module_name = 'actionsStats' let __setState = null;  export const bindStateMethods = (getState, setState, emit) =&gt; {   __setState = setState; }  export const initState = {   stats_array:[], }  export const listen = (name,data) =&gt;{   name === 'stats' &amp;&amp; updateStats(data); }  function updateStats(data) {   __setState({     stats_array:data.stats_array,   }) }<\/code><\/pre>\n<p>\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u043a \u0441\u0442\u043e\u0440\u0443<\/p>\n<pre><code class=\"javascript\">\/** src\/store.js *\/ ... import * as actionsStats from '.\/Stats\/actionsStats'  const { Provider, Connect } = createStore(   [     actionsWS,     actionsTimer,     actionsStats \/\/&lt;-- \u043c\u043e\u0434\u0443\u043b\u044c Stats   ] ); ...<\/code><\/pre>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u044d\u0442\u043e:<\/p>\n<figure class=\"float full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e0f\/920\/705\/e0f920705c6bf886ded016ecd50f1bb0.png\" width=\"620\" height=\"473\"><figcaption><\/figcaption><\/figure>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435 \u043c\u043e\u0434\u0443\u043b\u044c <code>Stats<\/code> \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u043e \u043d\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u044f <code>Timer<\/code>, \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u0442\u0440\u043e\u043a\u0438, \u0430 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. \u0427\u0442\u043e \u0435\u0441\u043b\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435, \u043d\u043e \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0445 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440? \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u043e\u0439.<\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f g_interval \u044d\u0442\u043e \u0443\u0433\u043b\u043e\u0432\u043e\u0439 \u043a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442 \u043d\u0430\u043a\u043b\u043e\u043d\u0430 \u043d\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u043a\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0439 \u0432\u0435\u043b\u0438\u0447\u0438\u043d\u044b. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0435\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0430\u0440\u0443 \u043a\u043d\u043e\u043f\u043e\u043a \u043a \u0433\u0440\u0430\u0444\u0438\u043a\u0443 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438. \u041f\u043b\u044e\u0441 \u0431\u0443\u0434\u0435\u0442 \u0443\u0432\u0435\u043b\u0438\u0447\u0432\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>interval<\/code> \u043c\u0438\u043d\u0443\u0441 \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0442\u044c.<\/p>\n<pre><code class=\"javascript\">\/** src\/Stats\/Stats.js *\/ ... import Buttons from '.\/Buttons' \/\/ \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044c ... const Stats = ({cxRun, stats_array})=&gt;&lt;div className={s.root}&gt;   &lt;div className={s.bars}&gt;     {stats_array.map((it,v)=&gt;&lt;Bar key={v} h={it} \/&gt;)}   &lt;\/div&gt;   &lt;Buttons\/&gt; {\/*\u041c\u043e\u0434\u0443\u043b\u044c \u043a\u043d\u043e\u043f\u043e\u0447\u043a\u0438*\/} &lt;\/div&gt; ...<\/code><\/pre>\n<p>\u0418 \u0441\u0430\u043c \u043c\u043e\u0434\u0443\u043b\u044c \u0441 \u043a\u043d\u043e\u043f\u043e\u0447\u043a\u0430\u043c\u0438<\/p>\n<pre><code class=\"javascript\">\/** src\/Stats\/Buttons.js *\/ import React from 'react'; import {Connect} from '..\/store' import s from '.\/Stats.module.css'  const DATA_INTERVAL_PLUS = {   name:'change_interval',   interval:1 } const DATA_INTERVAL_MINUS = {   name:'change_interval',   interval:-1 }  const Buttons = ({cxEmit, interval})=&gt;&lt;div className={s.root}&gt;   &lt;div className={s.btns}&gt;       &lt;button onClick={e=&gt;cxEmit('ws_send',DATA_INTERVAL_PLUS)}&gt;         plus       &lt;\/button&gt;        &lt;div className={s.len}&gt;interval:{interval}&lt;\/div&gt;        &lt;button onClick={e=&gt;cxEmit('ws_send',DATA_INTERVAL_MINUS)}&gt;         minus       &lt;\/button&gt;   &lt;\/div&gt; &lt;\/div&gt;  export default Connect(Buttons);<\/code><\/pre>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0430\u043d\u0435\u043b\u044c \u0441 \u043a\u043d\u043e\u043f\u043e\u0447\u043a\u0430\u043c\u0438:<\/p>\n<figure class=\"float full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6d6\/77c\/1ed\/6d677c1edbea3be74b63df446ff07c17.png\" width=\"619\" height=\"565\"><figcaption><\/figcaption><\/figure>\n<p>\u0418 \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c actionsWS.js<\/p>\n<pre><code class=\"javascript\">\/** src\/actionsWS.js *\/ ...  let wss = new WebSocket('ws:\/\/localhost:8888')  wss.onmessage = (msg) =&gt; {   let d = JSON.parse(msg.data);   __emit(d.name, d.data); }  \/\/ \"\u0441\u043b\u0443\u0448\u0430\u0435\u043c\" \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 export const listen = (name,data) =&gt; {   name === 'ws_send' &amp;&amp; sendMsg(data); } \/\/ \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 function sendMsg(msg) {   wss.send(JSON.stringify(msg)) }<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 <code>Buttons.js<\/code> \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 (<code>cxEmit<\/code>) \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 redoor. \u0421\u043e\u0431\u044b\u0442\u0438\u0435 <code>ws_send<\/code> &#171;\u0441\u043b\u0443\u0448\u0430\u0435\u0442&#187; \u043c\u043e\u0434\u0443\u043b\u044c <code>actionsWS.js<\/code>. \u041f\u043e\u043b\u0435\u0437\u043d\u0430\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 <code>data<\/code> &#8212; \u044d\u0442\u043e \u0434\u0432\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430: <code>DATA_INTERVAL_PLUS<\/code> \u0438 <code>DATA_INTERVAL_MINUS<\/code>. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u0430\u043c \u0435\u0441\u043b\u0438 \u043d\u0430\u0436\u0430\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0443 \u043f\u043b\u044e\u0441 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d  \u043e\u0431\u044a\u0435\u043a\u0442 <code>{ name:'change_interval',   interval:1 } <\/code><\/p>\n<p>\u041d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c <\/p>\n<pre><code class=\"javascript\">\/** src\/ws_server\/echo_server.js *\/ ...  wss.on('connection', function onConnect(ws) {   \/\/ \"\u0441\u043b\u0443\u0448\u0430\u0435\u043c\" \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \"change_interval\"   \/\/ \u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u044f Buttons.js   ws.on('message', function incoming(data) {     let d = JSON.parse(data);     d.name === 'change_interval' &amp;&amp; change_interval(d);   }); });  let g_interval = 1; \/\/ \u043c\u0435\u043d\u044f\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b function change_interval(data) {   g_interval += data.interval;   \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u0435, \u0447\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0438\u0437\u043c\u0435\u043d\u0435\u043d   sendToAll({name:'interval_changed', data:{interval:g_interval}}); }  ... <\/code><\/pre>\n<p>\u0418 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0448\u0442\u0440\u0438\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0442\u0440\u0430\u0437\u0438\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 Buttons.js. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 actionsStats.js \u043d\u0430\u0447\u043d\u0451\u043c \u0441\u043b\u0443\u0448\u0430\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u0435 &#171;<code>interval_changed<\/code>&#187; \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>interval<\/code><\/p>\n<pre><code class=\"javascript\">\/** src\/Stats\/actionsStats.js *\/ ...  export const initState = {   stats_array:[],   interval:1 \/\/ \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b }  export const listen = (name,data) =&gt;{   name === 'stats' &amp;&amp; updateStats(data);      \/\/ \"\u0441\u043b\u0443\u0448\u0430\u0435\u043c\" \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430   name === 'interval_changed' &amp;&amp; updateInterval(data); } \/\/ \u043e\u0431\u043d\u0430\u0432\u043b\u044f\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b function updateInterval(data) {   __setState({     interval:data.interval,   }) }  function updateStats(data) {   __setState({     stats_array:data.stats_array,   }) } <\/code><\/pre>\n<p>\u0418\u0442\u0430\u043a, \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0442\u0440\u0438 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u044f, \u0433\u0434\u0435 \u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0441\u043b\u0435\u0434\u0438\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 \u0441\u0432\u043e\u0438\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u0435\u043c \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0433\u043e. \u0427\u0442\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0443\u0434\u043e\u0431\u043d\u043e \u043a\u043e\u0433\u0434\u0430 \u0435\u0449\u0435 \u043d\u0435 \u044f\u0441\u043d\u0430 \u0434\u043e \u043a\u043e\u043d\u0446\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041d\u0430\u0434\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c, \u0447\u0442\u043e \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432\u0441\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438\u043c\u0435\u044e\u0442 \u0441\u043a\u0432\u043e\u0437\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0442\u043e \u043d\u0430\u0434\u043e \u0447\u0435\u0442\u043a\u043e \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043c\u044b \u0434\u043b\u044f \u0441\u0435\u0431\u044f \u0432\u044b\u0431\u0440\u0430\u043b\u0438 \u0442\u0430\u043a\u0443\u044e: (<code>MODULEN AME)_(FUNCTION NAME)_(VAR NAME)<\/code>. <\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c \u0431\u044b\u043b\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u043e.<a href=\"https:\/\/github.com\/rubender\/react_redoor_ws_example\" rel=\"noopener noreferrer nofollow\"> \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u043a\u043e\u0434\u044b <\/a>\u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u043e, \u043d\u0430 \u0433\u0438\u0442\u0445\u0430\u0431\u0435.<\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/557040\/\"> https:\/\/habr.com\/ru\/post\/557040\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p>\u0412 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043d\u0430\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432, \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 IPC (inter-process communication) \u043d\u0430  \u0441\u043e\u043a\u0435\u0442\u0430\u0445. \u0414\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442, \u0442\u043e\u0440\u0433\u043e\u0432\u043e\u0433\u043e \u0431\u043e\u0442\u0430, \u0433\u0434\u0435 \u0431\u044b\u043b\u0438 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u043a\u043e\u0442\u043e\u0440\u044b\u0435  \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u0438 \u0434\u0440\u0443\u0433 \u0441 \u0434\u0440\u0443\u0433\u043e\u043c.&nbsp; \u041f\u043e \u043c\u0435\u0440\u0435 \u0440\u043e\u0441\u0442\u0430 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0441\u0442\u0430\u043b \u0432\u043e\u043f\u0440\u043e\u0441 \u043e  \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0435, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445.&nbsp; \u041c\u044b \u0440\u0435\u0448\u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0432\u043e\u0435  \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f, \u043f\u043e\u0442\u043e\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0432\u0441\u0435\u0433\u043e \u0434\u0432\u0443\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u0445  <a href=\"https:\/\/github.com\/facebook\/react\" rel=\"noopener noreferrer nofollow\">react<\/a> \u0438 <a href=\"https:\/\/github.com\/rubender\/redoor\" rel=\"noopener noreferrer nofollow\">redoor<\/a>. \u042f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0432\u0430\u043c\u0438 \u043d\u0430\u0448\u0438\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c.<\/p>\n<p> \u041c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439 JSON \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438, \u0441 \u0434\u0432\u0443\u043c\u044f \u043f\u043e\u043b\u044f\u043c\u0438: \u0438\u043c\u044f \u0438 \u0434\u0430\u043d\u043d\u044b\u0435. \u0418\u043c\u044f &#8212; \u044d\u0442\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043a\u0430\u043a\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442 \u0438 \u043f\u043e\u043b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 &#8212; \u043f\u043e\u043b\u0435\u0437\u043d\u0430\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430. \u041f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"json\">{ name:'ticket_delete', data:{id:1} }<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u044b\u0440\u043e\u0439 \u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b \u043c\u0435\u043d\u044f\u043b\u0438\u0441\u044c \u043a\u0430\u0436\u0434\u0443\u044e \u043d\u0435\u0434\u0435\u043b\u044e, \u0442\u0430\u043a  \u0447\u0442\u043e \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0438 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u043c. \u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u043c\u044b\u0435 \u0435\u043c\u0443 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0442\u0430\u043a \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f, \u0443\u0434\u0430\u043b\u044f\u044f \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0430\u0431\u043e\u0440 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0432 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445. <\/p>\n<p>\u0418 \u0442\u0430\u043a \u043d\u0430\u0447\u043d\u0435\u043c.&nbsp;\u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0435\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0432\u0435\u0431 \u0441\u0435\u0440\u0432\u0435\u0440. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u0438\u0437 \u0442\u0440\u0435\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439. \u041d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435 \u043e\u043d\u0438 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043f\u0443\u043d\u043a\u0442\u0438\u0440\u043d\u044b\u043c\u0438 \u043b\u0438\u043d\u0438\u044f\u043c\u0438. \u0422\u0430\u0439\u043c\u0435\u0440, \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u0438 \u043a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u043e\u0439.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 Web Socket \u0441\u0435\u0440\u0432\u0435\u0440. <\/p>\n<pre><code class=\"javascript\">\/** src\/ws_server\/echo_server.js *\/  const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8888 });  function sendToAll( data) {   let str = JSON.stringify(data);   wss.clients.forEach(function each(client) {     client.send(str);   }); }  \/\/ \u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443 setInterval(e=&gt;{   let d = new Date();   let H = d.getHours();   let m = ('0'+d.getMinutes()).substr(-2);   let s = ('0'+d.getSeconds()).substr(-2);   let time_str = `${H}:${m}:${s}`;   sendToAll({name:'timer', data:{time_str}}); },1000);<\/code><\/pre>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 \u0434\u0430\u0442\u043e\u0439 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0441\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0438\u043c\u0441\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c. \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440:<\/p>\n<pre><code class=\"bash\">node src\/ws_server\/echo_server.js<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0414\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u0438 \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c rollup \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0438\u0436\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>rollup.config.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import serve from 'rollup-plugin-serve'; import babel from '@rollup\/plugin-babel'; import { nodeResolve } from '@rollup\/plugin-node-resolve'; import commonjs from '@rollup\/plugin-commonjs'; import hmr from 'rollup-plugin-hot' import postcss from 'rollup-plugin-postcss'; import autoprefixer from 'autoprefixer' import replace from '@rollup\/plugin-replace';  const browsers = [  \"last 2 years\",  \"&gt; 0.1%\",  \"not dead\"]  let is_production = process.env.BUILD === 'production';  const replace_cfg = {   'process.env.NODE_ENV': JSON.stringify( is_production ? 'production' : 'development' ),   preventAssignment:false, }  const babel_cfg = {     babelrc: false,     presets: [       [         \"@babel\/preset-env\",         {           targets: {             browsers: browsers           },         }       ],       \"@babel\/preset-react\"     ],     exclude: 'node_modules\/**',     plugins: [       \"@babel\/plugin-proposal-class-properties\",       [\"@babel\/plugin-transform-runtime\", {          \"regenerator\": true       }],       [ \"transform-react-jsx\" ]     ],     babelHelpers: 'runtime' }   const cfg = {   input: [     'src\/main.js',   ],   output: {     dir:'dist',     format: 'iife',     sourcemap: true,     exports: 'named',   },   inlineDynamicImports: true,   plugins: [     replace(replace_cfg),     babel(babel_cfg),     postcss({       plugins: [         autoprefixer({           overrideBrowserslist: browsers         }),       ]     }),     commonjs({         sourceMap: true,     }),     nodeResolve({         browser: true,         jsnext: true,         module: false,     }),     serve({       open: false,       host: 'localhost',       port: 3000,     }),   ], } ;   export default cfg; <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0422\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 <code>main.js<\/code> \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0435\u0433\u043e.<\/p>\n<pre><code class=\"javascript\">\/** src\/main.js *\/ import React, { createElement, Component, createContext } from 'react'; import ReactDOM from 'react-dom'; import {Connect, Provider} from '.\/store' import Timer from '.\/Timer\/Timer'  const Main = () =&gt; (   &lt;Provider&gt;     &lt;h1&gt;ws stats&lt;\/h1&gt;     &lt;Timer\/&gt;   &lt;\/Provider&gt; ); const root = document.body.appendChild(document.createElement(\"DIV\")); ReactDOM.render(&lt;Main \/&gt;, root);<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u043e\u0440 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<pre><code class=\"javascript\">\/** src\/store.js *\/ import React, { createElement, Component, createContext } from 'react'; import createStoreFactory from 'redoor'; import * as actionsWS from '.\/actionsWS' import * as actionsTimer from '.\/Timer\/actionsTimer'  const createStore = createStoreFactory({Component, createContext, createElement}); const { Provider, Connect } = createStore(   [     actionsWS,     \/\/ websocket actions     actionsTimer,  \/\/ Timer actions   ] ); export { Provider, Connect };<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u043e\u0434\u0443\u043b\u044c \u0442\u0430\u0439\u043c\u0435\u0440\u0430 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0430\u043a\u0448\u043d\u0435\u0441 \u0444\u0430\u0439\u043b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0441\u043e\u043a\u0435\u0442\u043e\u043c.<\/p>\n<pre><code class=\"javascript\">\/** src\/actionsWS.js *\/ export const  __module_name = 'actionsWS' let __emit; \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e emit \u043e\u0442 redoor export const bindStateMethods = (getState, setState, emit) =&gt; {   __emit = emit }; \/\/ \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 let wss = new WebSocket('ws:\/\/localhost:8888') \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0438\u0445 \u0432 \u043f\u043e\u0442\u043e\u043a redoor wss.onmessage = (msg) =&gt; {   let d = JSON.parse(msg.data);   __emit(d.name, d.data); } <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043d\u0430\u0434\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f \u043f\u043e\u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435. \u041d\u0430\u0448\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u043f\u043e\u043b\u044f\u043c\u0438: \u0438\u043c\u044f \u0438 \u0434\u0430\u043d\u043d\u044b\u0435. \u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 redoor \u043c\u043e\u0436\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u0442\u043e\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0438\u043c\u044f. \u0412\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u044d\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:<\/p>\n<pre><code>    +------+     | emit | --- events --+--------------+----- ... ------+-------------&gt; +------+              |              |                |                       v              v                v                  +----------+   +----------+     +----------+                  | actions1 |   | actions2 | ... | actionsN |                  +----------+   +----------+     +----------+<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0438\u043c\u0435\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c &#171;\u0441\u043b\u0443\u0448\u0430\u0442\u044c&#187; \u0441\u0432\u043e\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438 \u043f\u043e \u043d\u0430\u0434\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u0438 \u0447\u0443\u0436\u0438\u0435 \u0442\u043e\u0436\u0435. <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441\u0430\u043c \u043c\u043e\u0434\u0443\u043b\u044c \u0442\u0430\u0439\u043c\u0435\u0440\u0430. \u0412 \u043f\u0430\u043f\u043a\u0435 <code>Timer<\/code> \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u0432\u0430 \u0444\u0430\u0439\u043b\u0430 <code>Timer.js<\/code> \u0438 <code>actionsTimer.js<\/code><\/p>\n<pre><code class=\"javascript\">\/** src\/Timer\/Timer.js *\/  import React from 'react'; import {Connect} from '..\/store' import s from '.\/Timer.module.css'  const Timer = ({timer_str}) =&gt; &lt;div className={s.root}&gt;   {timer_str} &lt;\/div&gt;  export default Connect(Timer);<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0435 \u043f\u0440\u043e\u0441\u0442\u043e, \u0442\u0430\u0439\u043c\u0435\u0440 \u0431\u0435\u0440\u0435\u0442 \u0438\u0437 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0442\u0435\u0439\u0442\u0430 <code>timer_str<\/code> \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 <code>actionsTimer.js<\/code>. \u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>Connect<\/code> \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043c\u043e\u0434\u0443\u043b\u044c \u043a redoor.<\/p>\n<pre><code class=\"javascript\">\/** src\/Timer\/actionsTimer.js *\/ export const  __module_name = 'actionsTimer' let __setState;  \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0435\u0439\u0442\u0430 export const bindStateMethods = (getState, setState) =&gt; {   __setState = setState; };  \/\/ \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0442\u0430\u0439\u043c\u0435\u0440\u0430 export const initState = {   timer_str:'' }  \/\/ \"\u0441\u043b\u0443\u0448\u0430\u0435\u043c\" \u043f\u043e\u0442\u043e\u043a \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \"timer\" export const listen = (name,data) =&gt;{   name === 'timer' &amp;&amp; updateTimer(data); } \/\/ \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0435\u0439\u0442  function updateTimer(data) {   __setState({timer_str:data.time_str}) }<\/code><\/pre>\n<p>\u0412 \u0430\u043a\u0448\u0435\u0441 \u0444\u0430\u0439\u043b\u0435, \u043c\u044b &#171;\u0441\u043b\u0443\u0448\u0430\u0435\u043c&#187; \u0441\u043e\u0431\u044b\u0442\u0438\u0435 <code>timer<\/code> \u0442\u0430\u0439\u043c\u0435\u0440\u0430 (\u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>listen<\/code>) \u0438 \u043a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0435\u0439\u0442 \u0438 \u0432\u044b\u0432\u043e\u0434\u0438\u043c \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438.  <\/p>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0445 redoor:<\/p>\n<p> <code>__module_name<\/code> &#8212; \u0437\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043d\u0443\u0436\u043d\u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u043e\u043d\u0430 \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442 \u0432 \u043a\u0430\u043a\u043e\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0432\u0445\u043e\u0434\u044f\u0442 \u0430\u043a\u0448\u0435\u043d\u0441\u044b. <\/p>\n<p><code>bindStateMethods<\/code> &#8212; \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f <code>setState<\/code>, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0435\u0439\u0442\u0430.<\/p>\n<p><code>initState<\/code> &#8212; \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0438\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u044f \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e <code>timer_str<\/code><\/p>\n<p><code>listen<\/code>&#8212; \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u0432\u0441\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 redoor. <\/p>\n<p>\u0413\u043e\u0442\u043e\u0432\u043e. \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u044e \u0438 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <code>http:\/\/localhost:3000<\/code><\/p>\n<pre><code>npx rollup -c rollup.config.js --watch<\/code><\/pre>\n<p>\u0414\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0447\u0430\u0441\u0438\u043a\u0438 \u0441 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c. \u041f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u043e\u043c\u0443. \u041f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441 \u0442\u0430\u0439\u043c\u0435\u0440\u043e\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0449\u0435 \u043c\u043e\u0434\u0443\u043b\u044c \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 <code>echo_server.js<\/code><\/p>\n<pre><code class=\"javascript\">\/** src\/ws_server\/echo_server.js *\/  ... let g_interval = 1; \/\/ \u0414\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 setInterval(e=&gt;{   let stats_array = [];   for(let i=0;i&lt;30;i++) {     stats_array.push((Math.random()*(i*g_interval))|0);   }   let data  = {     stats_array   }   sendToAll({name:'stats', data}); },500);  ... <\/code><\/pre>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c \u0432 \u043f\u0440\u043e\u0435\u043a\u0442. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443 <code>Stats<\/code> \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c <code>Stats.js<\/code> \u0438 <code>actionsStats.js<\/code><\/p>\n<pre><code class=\"javascript\">\/** src\/Stats\/Stats.js *\/ import React from 'react'; import {Connect} from '..\/store' import s from '.\/Stats.module.css'  const Bar = ({h})=&gt;&lt;div className={s.bar} style={{height:`${h}`px}}&gt;   {h} &lt;\/div&gt;  const Stats = ({stats_array})=&gt;&lt;div className={s.root}&gt;   &lt;div className={s.bars}&gt;     {stats_array.map((it,v)=&gt;&lt;Bar key={v} h={it} \/&gt;)}   &lt;\/div&gt; &lt;\/div&gt;  export default Connect(Stats);<\/code><\/pre>\n<pre><code class=\"javascript\">\/** src\/Stats\/actionsStats.js *\/ export const  __module_name = 'actionsStats' let __setState = null;  export const bindStateMethods = (getState, setState, emit) =&gt; {   __setState = setState; }  export const initState = {   stats_array:[], }  export const listen = (name,data) =&gt;{   name === 'stats' &amp;&amp; updateStats(data); }  function updateStats(data) {   __setState({     stats_array:data.stats_array,   }) }<\/code><\/pre>\n<p>\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u043a \u0441\u0442\u043e\u0440\u0443<\/p>\n<pre><code class=\"javascript\">\/** src\/store.js *\/ ... import * as actionsStats from '.\/Stats\/actionsStats'  const { Provider, Connect } = createStore(   [     actionsWS,     actionsTimer,     actionsStats \/\/&lt;-- \u043c\u043e\u0434\u0443\u043b\u044c Stats   ] ); ...<\/code><\/pre>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u044d\u0442\u043e:<\/p>\n<figure class=\"float full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435 \u043c\u043e\u0434\u0443\u043b\u044c <code>Stats<\/code> \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u043e \u043d\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u044f <code>Timer<\/code>, \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u0442\u0440\u043e\u043a\u0438, \u0430 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. \u0427\u0442\u043e \u0435\u0441\u043b\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435, \u043d\u043e \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0445 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440? \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u043e\u0439.<\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f g_interval \u044d\u0442\u043e \u0443\u0433\u043b\u043e\u0432\u043e\u0439 \u043a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442 \u043d\u0430\u043a\u043b\u043e\u043d\u0430 \u043d\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u043a\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0439 \u0432\u0435\u043b\u0438\u0447\u0438\u043d\u044b. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0435\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0430\u0440\u0443 \u043a\u043d\u043e\u043f\u043e\u043a \u043a \u0433\u0440\u0430\u0444\u0438\u043a\u0443 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438. \u041f\u043b\u044e\u0441 \u0431\u0443\u0434\u0435\u0442 \u0443\u0432\u0435\u043b\u0438\u0447\u0432\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>interval<\/code> \u043c\u0438\u043d\u0443\u0441 \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0442\u044c.<\/p>\n<pre><code class=\"javascript\">\/** src\/Stats\/Stats.js *\/ ... import Buttons from '.\/Buttons' \/\/ \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044c ... const Stats = ({cxRun, stats_array})=&gt;&lt;div className={s.root}&gt;   &lt;div className={s.bars}&gt;<\/code><\/pre>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-323014","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/323014","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=323014"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/323014\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=323014"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=323014"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=323014"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}