Skip to content

Instantly share code, notes, and snippets.

@danielrob
Created May 14, 2020 02:06
Show Gist options
  • Save danielrob/777821c429503f9cbd02e0a940065c1c to your computer and use it in GitHub Desktop.
Save danielrob/777821c429503f9cbd02e0a940065c1c to your computer and use it in GitHub Desktop.
Redux Input
import { toPath } from "lodash";
import { is, path, assocPath, always } from "ramda";
import React, { useContext } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Context, ReduxInputContext } from "./ReduxInputContext";
/**
* ReduxInput connects any standard input component to the redux store where a
* "standard input component" is any component accepting e.g. value and onChange props.
*
* @param {function} Component - the component to connect to the store.
* @param {function} selector - selects state containing the input value to be acted on from the redux store.
* @param {function} action - the action to dispatch whenever the underlying onChange is fired.
* @param {string|array} actOn - sub-path of the selected state to read/write the input value to.
* @param {function} parse - a transformator to transform raw onChange value to store value.
* @param {function} format - a transformator to transform store value to raw input value.
* @param {object} props - props to be explicitly passed to the underlying Component
*/
const ReduxInput = ({
Component,
selector,
action,
actOn,
parse,
format,
props,
...rest
}) => {
const dispatch = useDispatch();
const { cSelector, cAction } = useContext(Context) || {};
const state = useSelector(selector || cSelector || fallbackSelector);
const value = actOn ? path(toPath(actOn), state) : state;
const formattedValue = is(Function, format) ? format(value) : value;
const callback = (onChangeValue) => {
const eventValue = onChangeValue?.target?.value;
const nextValue = eventValue === undefined ? onChangeValue : eventValue;
const parsedValue = is(Function, parse) ? parse(nextValue) : nextValue;
const finalPayload = actOn
? assocPath(toPath(actOn), parsedValue, state)
: parsedValue;
if (is(Function, action || cAction)) {
dispatch((action || cAction)(finalPayload));
}
};
return (
<Component
value={formattedValue}
onChange={callback}
{...props}
{...rest}
/>
);
};
const fallbackSelector = always({});
export { ReduxInputContext, ReduxInput };
import React from "react";
const Context = React.createContext();
const ReduxInputContext = ({
selector,
action,
children,
Component = React.Fragment,
...props
}) => (
<Component {...props}>
<Context.Provider value={{ cSelector: selector, cAction: action }}>
{children}
</Context.Provider>
</Component>
);
export { ReduxInputContext, Context };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment