Skip to content

Instantly share code, notes, and snippets.

@JoviDeCroock
Created June 4, 2019 07:55
Show Gist options
  • Save JoviDeCroock/4f5cc37442209800ed31c1658cb7f114 to your computer and use it in GitHub Desktop.
Save JoviDeCroock/4f5cc37442209800ed31c1658cb7f114 to your computer and use it in GitHub Desktop.
import * as React from 'react';
import { connect } from 'react-redux';
import { sendMutationAction } from '@bucket/actions/genericActions';
import { getMutationDataFactorySelector } from '@bucket/selectors/genericSelectors';
/**
* This component has the sole purpose of executing a Mutation
* to achieve this it will go through some steps
*
* - Render: It will bind a function according to the passed parameters.
* Secondly it will memoize the render function to prevent friction.
* - Execution: Will indicate current namespace with hashed vars as in-flight.
* - Processing: Will use api.fetch to postprocess the response.
* - Post: Will execute the refetchQueries to achieve the new state.
* Optionally when it's a form it will also execute the passed callback.
*/
const Mutation = ({ children, mutation, data, namespace, refetchQueries, sendMutation, isDxApi }) => {
const mutate = React.useCallback((variables) => {
sendMutation({
mutation: typeof mutation === 'function' ? mutation(variables) : mutation,
namespace: typeof namespace === 'function' ? namespace(variables) : namespace,
variables,
refetchQueries,
isDxApi,
});
}, [isDxApi, mutation, namespace, refetchQueries, sendMutation]);
return React.useMemo(() =>
children(mutate, { data }), [children, mutate, data]);
};
/**
* Why do we do this?
* To prevent people from abusing this component in
* such a fashion that a consistent state can't be guaranteed
*
* Example:
* <Mutation> --> Memoizes X
* <Mutation> --> Memoized Y
* ...
* </Mutation>
* </Mutation>
*
* On next iteration we bounce on Mutation #1 with a memoized Y implying a perf
* nett loss.
*/
const makeMapState = () => {
const selectItemForThisComponent = getMutationDataFactorySelector();
return (state, { namespace, variables }) => ({
data: selectItemForThisComponent(state, { namespace, variables }),
});
};
// One could wonder why we use an additional memo when our redux state has already
// achieved a state of individual memoization.
// Well when a parent rerenders it will invoke children
// In this case we have:
// <Mutation>{children}</Mutation>
// Implying that when a parent rerenders our Mutation and render prop
// can be skipped on equality --> only the childComponent will rerender
// With equal Mutation context.
export default connect(makeMapState, {
sendMutation: sendMutationAction,
})(Mutation);
import * as React from 'react';
import { fetchQueryAction } from '@bucket/actions';
import { getQueryDataFactorySelector } from '@bucket/selectors/genericSelectors';
import { connect } from 'react-redux';
// Guidelines for optimal performance
// If you always pass in a fresh object as variables
// You will keep querying new data so DONT
// This can be done through useMemo
// Another option would be to pass an argument just like the second
// argument of useEffect to this Render Props func.
const Query = ({ children, data, fetchQuery, query, namespace, variables, skip, isDxApi }) => {
const [counter, setCounter] = React.useState(0);
React.useEffect(() => {
if (!skip) {
fetchQuery({ query, namespace, variables, isDxApi });
}
}, [variables, counter, skip]); // eslint-disable-line
const refetch = React.useCallback(() => setCounter(i => i + 1), [setCounter]);
return React.useMemo(() => children(data, refetch), [children, data, refetch]);
};
// Selector factory for optimal performance.
// This ensures that every Query gets a personalized
// memoized selector.
const makeMapState = () => {
const selectItemForThisComponent = getQueryDataFactorySelector();
return (state, { namespace, variables, skip }) => ({
data: selectItemForThisComponent(state, { namespace, variables, skip }),
});
};
export default connect(makeMapState, {
fetchQuery: fetchQueryAction,
})(Query);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment