@applicaster/zapp-react-native-redux

Redux store & features for the Quick Brick Zapp React-Native app

Usage no npm install needed!

<script type="module">
  import applicasterZappReactNativeRedux from 'https://cdn.skypack.dev/@applicaster/zapp-react-native-redux';
</script>

README

Zapp React Native Redux

CircleCI npm version

logo

This package contains the Zapp React Native Redux code.

Available reducers

all these reducers can be used by any plugin or component, by using the connectToStore function above

styles: Available styles for the app. Color codes are fixed for React Native, and style properties are grouped in a more structured way, for more convenient use than the native side's flat map of keys.

rivers: Rivers configured for the app. Only change is the array from layout.json is changed to a map where each river is accessible with its id.

plugins: Array of plugins available in the app. No transformation is applied when this data is loaded.

pluginConfigurations: Map of plugin configuration. For each plugin identifier, this reducer contains the plugin_configuration object coming from zapp, including the configuration_json property, with the configuration set by zapp users.

remoteConfigurations: Custom remote configurations properties available in Zapp.

components: Map of available components for the app. No transformation is applied when this data is loaded.

appSettings: AppSettings data injected in the ZappApp component. No transformation is applied when this data is loaded.

zappPipes: The Zapp Pipes reducer keeps track of the feed data used by the app. A loadPipesData: (dataSourceUrl: string, clearCache: bool = false) => void action creator is available to manually load a datasource url in the store. This function is used by the ZappPipesDataConnector decorator which automatically retrieves data from feeds for ui_components. But it can also be used manually in plugins for instance, or in components, in order to implement features like pull-to-refresh, or automatic reload of the data... When a datasource is retrieved through this action, it is stored in the reducer with the datasource url as key, and the returned data as value. Requesting an already fetched datasource url will skip the request, unless the clearCache option is set to true.

Main module

The main module exports utility functions to interact with the redux store :

createZappReduxStore(config<Object>): ReduxStore

Creates the redux store Available options are :

  • additionalReducers<Object> : optional map of additional reducers to configure the store. Some names are reserved and will be ignored (styles, rivers, plugins, components, appSettings, zappPipes)
  • additionalMiddlewares<Array<ReduxMiddleware>>: optional array of middlewares to add - by default, already includes thunk in development env
  • initialState<Object> : optional override of initialState (defaults to empty object)
  • env<String>: optional override to define a specific env. will automatically resolve to development | production |test accordingly

NB : If you experience a crash when calling this function without any option, try calling it with an empty object instead

import { createZappReduxStore, loadAppContextData, connectToStore } from "@applicaster/zapp-react-native-redux";
import fooReducer from "./reducers/Foo";
import { customMiddleware, otherCustomMiddleware } from "./middlewares/MyCustomMiddlewares";

const store = createZappReduxStore({
  addedReducers: { foo: fooReducer }, // optional - map of additional reducers to add to the store
  addedMiddlewares: [customMiddleware, otherCustomMiddleware], // optional - array of additional reducers - already includes thunk
  initialState = {}, // optional initialState for the store,
  env: __DEV__ ? "development" : "production" // optional env. Defaults to the value here, but left for testing purposes or other custom settings
});

// now let's say we want to inject styles, rivers, and a components list to the store
// this function requires access to the store's dispatch function, so it can be used only in a connected component or a middleware
const styles = { /* .. */ };
const rivers = { /* .. */ };
const components = { /* .. */ };
loadAppContextData(dispatch, { styles, rivers, components });

// and last but not least, let's create a connector function to add styles and rivers to a component
// the example below will add 3 props : styles, rivers, and a customReduxAction bound to redux's dispatcher
import UnconnectedComponent from "./components/UnconnectedComponent";
const connector = connectToStore(state => ({ styles: state.styles, rivers: state.rivers }), { customReduxAction });
const MyConnectedComponent = connector(UnconnectedComponent);

loadAppContextData(dispatch<Function>, appContextMap<Object>)

In the Redux store, context data is made of styles, rivers, plugins, components, and appSettings. The data from these reducers are read-only. Data can be replaced in the reducer by using the loadAppContextData function declared above. When using this function, depending on the context data you are loading, a specific loader will be invoked to format the incoming data for a more convenient use in the redux store. These loaders are fairly straight forward - refer to the source code to see more details about the transformation applied.

This function loads contextual data in the redux store. multiple contexts can be loaded at once. For instance, in order to load, styles and plugins contextual data :

const styles = ...
const plugins = ...

const store = createZappReduxStore({});

loadAppContextData(store.dispatch, { styles, plugins });

This function is used when initializing the app, but can also be used later on to update the app contextual data.

connectToStore(mapDispatchToProps<Function>, actionsMap<Object>)

Returns a react-redux connector to connect a component to the store. This is simply a shorthand function that allows a simpler and shorter syntax for common patterns

import { MyComponent } from "./MyComponent";
import { connectToStore } from "@applicaster/zapp-react-native-redux";
import { pick } from "ramda";
import { fooAction } from "fooAction";

export default connectToStore(
  pick(["styles", "rivers]),
  { fooAction }
)(MyComponent);

Hooks

useActions

Identical implementation, as described here: https://react-redux.js.org/api/hooks#recipe-useactions

import { useActions } from "@applicaster/zapp-react-native-redux/hooks";

const actionCreator = (payload) => {
  type: "test", payload;
};

const Comp = () => {
  const actions = useActions({ testAction: actionCreator });

  actions.testAction("fooBar");
};

usePickFromState

Returns specified key/keys from the store.

Call signature: (keys, equalityFn) => any

keys: key or list of keys to pick from the state. If empty, return entire redux state equalityFn: (prev: any, next: any) => boolean function to compare picked data between the renders. Returning true will stop component from re-rendering and hook will return a previous, cached result. As a default, a shallow equality check is done.

import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";

// redux state {foo: 'value', bar: 'value', foobar: 'value}
const Comp = () => {
  const singleKey = usePickFromState("foo"); // singleKey: { foo: 'value' }
  const multipleKeys = usePickFromState(["foo", "bar"]); // multipleKeys: { foo: 'value', bar: 'value' }
  const noArguments = usePickFromState(); // noArguments: {foo: 'value', bar: 'value', foobar: 'value'}
};

useRedux

Hook that aims to replace connect function providing similar functionality Call signature: useRedux({mapStateToProps, actions, deps})

mapStateToProps: (state) => any state selector function that provides state in a first argument

actions: action to be wrapped in dispatch function creators {myAction: () => {type: 'test' }}

deps: watchers passed to useActions hook, in order to re-bind actions on a watcher change.

import { usRedux } from "@applicaster/zapp-react-native-redux/hooks";


const myAction = () => {type: 'action'}
const myAction2 = () => {type: 'action2'}

const Comp = () => {
  const {state, dispatch, myAction: action, myAction2: action2} = useRedux({
    mapStateToProps: state => state.foo,
    actions: [{myAction2}, {myAction}],
  );
};