All Downloads are FREE. Search and download functionalities are using the official Maven repository.

amework.n2o-react.7.28.2.source-code.store.js Maven / Gradle / Ivy

The newest version!
import thunkMiddleware from 'redux-thunk'
import createSagaMiddleware from 'redux-saga'
import { createLogger } from 'redux-logger'
import { routerMiddleware } from 'connected-react-router'
import { configureStore } from '@reduxjs/toolkit'

import generateReducer from './reducers'
import generateSagas from './sagas'
import { updateModel } from './ducks/models/store'

const sagaMiddleware = createSagaMiddleware()

export default (initialState, history, config = {}) => {
    const [squasherMiddleware, squasherStorePatcher] = createSquasher([
        updateModel.type,
        'n2o/models/SET',
        'n2o/widgets/CHANGE_PAGE',
        'n2o/widgets/DATA_SUCCESS',
        'n2o/widgets/RESOLVE',
        // Странный баг, что дровер рендерится, когда его уже быть не должно. Нужно синхронно обрабатывать этот экшен
        'n2o/overlays/DESTROY',
        'n2o/overlays/DESTROY_OVERLAYS',
    ])

    const middlewares = [
        thunkMiddleware,
        ({ getState }) => (next) => {
            let prevState = {}

            return (action) => {
                let nextAction = action

                if (Object.prototype.toString.call(action) === '[object Object]') {
                    const { payload = {}, meta = {}, ...actionFields } = action

                    nextAction = { ...actionFields, payload, meta: { ...meta, prevState } }
                }

                prevState = getState()

                return next(nextAction)
            }
        },
        sagaMiddleware,
        routerMiddleware(history),
        squasherMiddleware,
    ]

    if (process.env.NODE_ENV === 'development') {
        const loggerMiddleware = createLogger()

        middlewares.push(loggerMiddleware)
    }

    const store = configureStore({
        reducer: generateReducer(history, config.customReducers),
        preloadedState: initialState,
        middleware: middlewares,
        devTools: true,
    })

    squasherStorePatcher(store)

    sagaMiddleware.run(generateSagas(store.dispatch, config))

    return store
}

/**
 * По умолчанию, redux синхронно вызывает своих слушателей (connect`ы) для каждого экшена,
 * соответственно, при большом количестве синхронных экшенов мы получаем множество синхронных ререндеров
 * СКОРЕЕ ВСЕГО промежуточные ререндеры не нужны. Исходя из этой вводной мы можем схлопнуть почти все
 * синхронные экшены, а экшены, после которых обязательно нужен ререндер - передаём параметром
 */
function createSquasher(nonSquashedActions = []) {
    // Накапливать ли изменения синхронно поступающих экшенов
    let squashing = true

    // Таймер для отложенного вызова подписчиков стора
    let timer = null

    // Колбэки подписчиков
    const listeners = []

    nonSquashedActions = nonSquashedActions.reduce((nonSquashedActions, action) => {
        nonSquashedActions[action] = true

        return nonSquashedActions
    }, {})

    return [
        () => next => (action) => {
            const { type } = action

            squashing = true

            if (type && nonSquashedActions[type]) {
                squashing = false
            }

            return next(action)
        },

        function storePatcher(store) {
            // Первыми подписываемся на стор. Что-бы больше ни кто напрямую не смог подписаться - переопределяем
            // встроенный метод подписки.
            store.subscribe(() => {
                if (squashing) {
                    if (timer) {
                        return
                    }

                    timer = setTimeout(callListeners, 0)
                } else {
                    callListeners()
                }

                function callListeners() {
                    const currentListeners = [...listeners]

                    clearTimeout(timer)
                    timer = null

                    for (let i = 0; i < currentListeners.length; i++) {
                        const listener = currentListeners[i]

                        listener()
                    }
                }
            })

            // Заменяем стандартный метод подписки на изменения, чтоб самим контролировать когда дергать подписчиков
            store.subscribe = (listener) => {
                let isSubscribed = true

                listeners.push(listener)

                return function unsubscribe() {
                    if (!isSubscribed) {
                        return
                    }

                    isSubscribed = false

                    const index = listeners.indexOf(listener)

                    listeners.splice(index, 1)
                }
            }

            return store
        },
    ]
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy