Created
June 4, 2019 07:55
-
-
Save JoviDeCroock/4f5cc37442209800ed31c1658cb7f114 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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