') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - 9technology/redux-namespaces: Namespaces for Redux actions, reducers and state. · GitHub
Skip to content

Repository files navigation

Redux Namespaces

Build StatusCoverage Status

Namespace Redux actions, reducers and state.

Usage


Build re-usable components in React, or any other UI library, using Redux adds complexity. Namespacing actions, reducers and state allows multiple instances of components to co-exist with independent state.

Read more: https://www.nine.com.au/tech/2017/07/13/09/40/redux-namespaces

Reducers

When handling multiple components and computing derived data an application's reducer must be aware of this nature. Usually when reducing the action it passes some meta data. This meta data sometimes changes the returning state structure.

Using createReducer(), reducers can be written without the need of this meta data. For example, our state has two superhereos and the application sets their side kick. The reducer is written as if it only had a single superhero. So no longer do reducers have to be instance aware. Regardless if it's batman or superman the reducer will set the side kick.

import{createStore}from'redux';import{createReducer}from'redux-namespaces';// Always set the initial state, see Caveats belowconstinitial={batman: {},superman: {}};// Generic superhero reducerconstsuperheroReducer=(state,action)=>{switch(action.type){case'SET_SIDE_KICK':
return{
...state,sideKick: action.sideKick,};default:
returnstate;}};// Make the generic reducer namespace awareconstreducer=createReducer(superheroReducer);conststore=createStore(reducer,initial);

Action Creators

Action creators now don't require the meta data for the reducer. Since the reducer is built for a single superhero actions can now follow the same pattern.

constactions={sideKick(sideKick){return{type: 'SET_SIDE_KICK',
sideKick,};}};

Use createActions() with a given state namespace and the actions list to generate action creators.

import{createActions}from'redux-namespaces';// Namespace actionsconstbatman=createActions('batman',actions);constsuperman=createActions('superman',actions);

batman and superman can now disptach the same actions to the reducer but state independent.

// Dispatchstore.dispatch(batman.sideKick('Robin'));store.dispatch(superman.sideKick('Jimmy Olsen'));store.getState();// { batman: { sideKick: 'Robin' }, superman: { sideKick: 'Jimmy Olsen'} }

React

Combine Redux Namespaces with React following the usual react-redux setup. However use createActions for mapDispatchToProps().

constmapDispatchToProps=dispatch=>bindActionCreators(createActions('namespace',{propAction(){return{type: 'action'};}}));

Caveats


Initial namespaces must be set within the initial state otherwise action creators will dispatch to an undefined property within the state.

API


createActions(namespace, actions)

  • namespaceString The namespace for the action creators.
  • actionsObject Action creators to namespace.

Returns namespaced action creators.

createReducers(...reducers)

  • reducersFunction|[]Function Redux reducer.

Returns wrapped reducer that is namespace aware.

License


BSD-3-Clause

Copyright (c) 2017 9Technology

About

Namespaces for Redux actions, reducers and state.

Topics

Resources

Stars

7 stars

Watchers

8 watching

Forks

Releases

Packages

Used by

Contributors

Languages