Coder Social home page Coder Social logo

Comments (6)

mike1808 avatar mike1808 commented on May 29, 2024 2

I'll like to add one use case for this feature. As Redux based application grows there are several solutions to make it load faster:

  1. have several apps
  2. code split modules/pages

In our company, we're doing 2. so in order to use the redux-beacon we need to have the ability to lazy load the events reducers.

For initial app load we fetch only actions/thunks/components that are required for that page. When a user navigates to another page we lazy-load their required files.

If we are going to use redux-beacon as is, we will introduce a dependency graph to all trackable actions that will cause including all that files with actions to the initial load chunk (we're using Webpack) and make the initial JS file quite big. So, having something similar to redux#replaceReducer will help to solve the issue.

from redux-beacon.

ttmarek avatar ttmarek commented on May 29, 2024

huh, interesting. Are you doing something like:

if user x, track these analytics, else if user y, track these analytics

from redux-beacon.

cwayfinder avatar cwayfinder commented on May 29, 2024

No. We need to modify the EventDefinitionsMap in the application without rebuild. Sometimes we need to change an event definition and we just don't want to wait for a new release of our app to do that.

from redux-beacon.

cwayfinder avatar cwayfinder commented on May 29, 2024

The only solution I see now is to fetch the configuration from the server as JSON (instead of using your powerful EventDefinitionsMap) via XHR. And create my own meta reducer to apply static definitions declared in that JSON.

from redux-beacon.

ttmarek avatar ttmarek commented on May 29, 2024

@cwayfinder

Sorry for the late reply on this. Had a busy week work-wise last week, and am in the middle of a move so my evenings/weekends are a bit more busy than usual.

I'm not sold on adding support for this sort of behaviour to the main redux-beacon package. Mostly because it seems like a fairly unique use-case. Is that fair?

But...we should be able to design something pretty easily based on the existing createMiddleware and createMetaReducer functions.

Maybe something like:

const getEvents = (eventsMap: EventsMap | EventsMapper) =>
    typeof eventsMap === 'function'
      ? action => flatten<EventDefinition>([eventsMap(action)])
      : action => getEventsWithMatchingKey(eventsMap, action.type);

function createMiddlewareDynamic(
  eventsMap: EventsMap | EventsMapper,
  target: Target,
  extensions: Extensions = {}
) {
  // save the initial (default) events definitions map
  let currentEventsMap = eventsMap;

  // a function that we expose to update currentEventsMap
  const updateEventsMap = (newEventsMap: EventsMap | EventsMapper) => {
     currentEventsMap = newEventsMap;
  };

  // the middleware
  const middleware = store => next => action => {
    const prevState = store.getState();
    const result = next(action);
    const nextState = store.getState();

    const events = createEvents(
      getEvents(currentEventsMap)(action),
      prevState,
      action,
      nextState
    );

    registerEvents(events, target, extensions, prevState, action, nextState);

    return result;
  };

  return { middleware, updateEventsMap };
}

And you'd use the function like so:

const { middleware, updateEventsMap } = createMiddlewareDynamic(initialEventsMap, gtmTarget(), { logger })

// use updateEventsMap wherever
updateEventsMap(newEventsMap)

Also, the function above would require access to the following utility functions in the main redux-beacon package:

import createEvents from './create-events';
import getEventsWithMatchingKey from './get-events-with-matching-key';
import registerEvents from './register-events';

I don't think I expose these. So for now, if you want to test it out I would copy-paste the compiled versions of these functions. If you need help with that, let me know.

from redux-beacon.

ygr1k avatar ygr1k commented on May 29, 2024

Thanks all for the comments on this issue!
I'll share my implementation for code splitting in pure JS based on https://github.com/dbartholomae/redux-dynamic-modules-beacon

class EventsManager {
  #eventsMaps = [];

  getEventsMap = action => {
    const eventsMapByAction = this.#eventsMaps
      .map(eventMapOrMapper =>
        typeof eventMapOrMapper === 'function'
          ? eventMapOrMapper(action)
          : eventMapOrMapper[action.type],
      )
      .flat();

    return eventsMapByAction;
  };

  addEventsMap(eventMapOrMapper) {
    this.#eventsMaps.push(eventMapOrMapper);
  }

  removeEventsMap(eventMapOrMapper) {
    this.#eventsMaps = this.#eventsMaps.filter(map => map !== eventMapOrMapper);
  }
}
import { createMiddleware } from 'redux-beacon';

function createManageableMiddleware(eventsMap, target, extensions) {
  const manager = new EventsManager();
  manager.addEventsMap(eventsMap);

  // Setup the event middleware
  const middleware = createMiddleware(manager.getEventsMap, target, extensions);

  return {
    middleware,
    manager,
  };
}
//enhance store with manageable middleware + attach events manager to the redux store in configureStore func
store.eventsManager = manageableMiddleware.manager;
// hook for attach/detach events
import { useStore } from 'react-redux';
import { useEffect, useRef } from 'react';
import { isNull } from 'lodash';

function useEventsMap(eventsMap) {
  const store = useStore();
  const eventsMapWasAttached = useRef(null);

  useEffect(() => {
    if (isNull(eventsMapWasAttached.current)) {
      store.eventsManager.addEventsMap(eventsMap);
      eventsMapWasAttached.current = true;
      console.log(`useEventsMap, events map attached:`, eventsMap);
    }

    return () => {
      if (eventsMapWasAttached.current) {
        store.eventsManager.removeEventsMap(eventsMap);
        eventsMapWasAttached.current = null;
        console.log(`useEventsMap, events map detached:`, eventsMap);
      }
    };
  }, []);
}
//usage example
import {trackEvent} from "@redux-beacon/google-analytics-gtag";
const eventsMap = {
    [ON_ACTION_TYPE_CONST_SUBMIT_THIS_EVENT]: trackEvent(() => {...}),
};

function SomeComponent(){
    //on did mount life cycle attach events from this map, and detach it on unmount
    useEventsMap(eventsMap);

    return (
        ...
    )
}

from redux-beacon.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.