Add custom React.memo comparator aware of fragment references
This commit is contained in:
Родитель
73528af5be
Коммит
316b55dcad
|
@ -15,7 +15,7 @@ import {
|
|||
useApolloClient,
|
||||
useQuery as useApolloQuery,
|
||||
} from "@apollo/client";
|
||||
import { useExecuteAndWatchQuery } from "./useExecuteAndWatchQuery";
|
||||
import { useExecuteAndWatchQuery } from "./move-to-libs/useExecuteAndWatchQuery";
|
||||
import {
|
||||
executionQueryDocument,
|
||||
watchQueryDocument,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useCallback } from "react";
|
||||
import { useFragment } from "@graphitation/apollo-react-relay-duct-tape";
|
||||
import { graphql } from "@graphitation/graphql-js-tag";
|
||||
import { shallowCompareFragmentReferences } from "./move-to-libs/shallowCompareFragmentReferences";
|
||||
|
||||
import useChangeTodoStatusMutation from "./useChangeTodoStatusMutation";
|
||||
|
||||
|
@ -60,5 +61,5 @@ const Todo: React.FC<{ todo: Todo_todoFragment$key }> = ({ todo: todoRef }) => {
|
|||
|
||||
(Todo as any).whyDidYouRender = true;
|
||||
|
||||
const MemoizedTodo = React.memo(Todo);
|
||||
const MemoizedTodo = React.memo(Todo, shallowCompareFragmentReferences("todo"));
|
||||
export { MemoizedTodo as Todo };
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { useFragment } from "@graphitation/apollo-react-relay-duct-tape";
|
||||
import { graphql } from "@graphitation/graphql-js-tag";
|
||||
import { shallowCompareFragmentReferences } from "./move-to-libs/shallowCompareFragmentReferences";
|
||||
|
||||
import {
|
||||
TodoList_todosFragment$key,
|
||||
|
@ -54,5 +55,8 @@ const TodoList: React.FC<{ todos: TodoList_todosFragment$key }> = ({
|
|||
|
||||
(TodoList as any).whyDidYouRender = true;
|
||||
|
||||
const MemoizedTodoList = React.memo(TodoList);
|
||||
const MemoizedTodoList = React.memo(
|
||||
TodoList,
|
||||
shallowCompareFragmentReferences("todos")
|
||||
);
|
||||
export { MemoizedTodoList as TodoList };
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { useFragment } from "@graphitation/apollo-react-relay-duct-tape";
|
||||
import { graphql } from "@graphitation/graphql-js-tag";
|
||||
import { shallowCompareFragmentReferences } from "./move-to-libs/shallowCompareFragmentReferences";
|
||||
|
||||
import {
|
||||
TodoListFooter_todosFragment$key,
|
||||
|
@ -17,7 +18,7 @@ export const TodoListFooter_todosFragment = graphql`
|
|||
}
|
||||
`;
|
||||
|
||||
export const TodoListFooter: React.FC<{
|
||||
const TodoListFooter: React.FC<{
|
||||
todos: TodoListFooter_todosFragment$key;
|
||||
}> = ({ todos: todosRef }) => {
|
||||
// TODO: This needs to be replaced by the webpack loader
|
||||
|
@ -43,3 +44,9 @@ export const TodoListFooter: React.FC<{
|
|||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
const MemoizedTodoListFooter = React.memo(
|
||||
TodoListFooter,
|
||||
shallowCompareFragmentReferences("todos")
|
||||
);
|
||||
export { MemoizedTodoListFooter as TodoListFooter };
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# TODO
|
||||
|
||||
These need to move into our libs
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Modified from https://github.com/facebook/react/blob/201af81b0168cabea3cc07cd8201378a4fec4aaf/packages/shared/shallowEqual.js
|
||||
* Copying is the suggested way, as mentioned here https://github.com/facebook/react/issues/16919
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found at
|
||||
* https://github.com/facebook/react/blob/201af81b0168cabea3cc07cd8201378a4fec4aaf/LICENSE
|
||||
*/
|
||||
|
||||
import invariant from "invariant";
|
||||
|
||||
/**
|
||||
* A custom React.memo() comparator function factory that can be used with
|
||||
* components that use `useFragment` on a GraphQL type that implements the
|
||||
* `Node` interface, in which case only the `id` value needs to be equal to
|
||||
* avoid a re-render.
|
||||
*
|
||||
* @todo
|
||||
* Support arrays with fragment references
|
||||
*
|
||||
* @param fragmentReferenceProps
|
||||
* The props that refer to fragment references and should only be compared by
|
||||
* their [Node] ids.
|
||||
*
|
||||
* @returns
|
||||
* A comparator with parameters typed such that TS can verify the component
|
||||
* passed to React.memo() has props that match.
|
||||
*/
|
||||
export function shallowCompareFragmentReferences<K extends string>(
|
||||
...fragmentReferenceProps: K[]
|
||||
) {
|
||||
return (prevProps: Record<K, any>, nextProps: Record<K, any>) => {
|
||||
if (Object.is(prevProps, nextProps)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const keysPrev = Object.keys(prevProps);
|
||||
const keysNext = Object.keys(nextProps);
|
||||
|
||||
if (keysPrev.length !== keysNext.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < keysPrev.length; i++) {
|
||||
const checkKey = keysPrev[i] as K;
|
||||
if (
|
||||
!nextProps.hasOwnProperty(checkKey) ||
|
||||
fragmentReferenceProps.includes(checkKey)
|
||||
? !idsEqual(prevProps[checkKey], nextProps[checkKey])
|
||||
: !Object.is(prevProps[checkKey], nextProps[checkKey])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
function idsEqual(objA: { id?: any }, objB: { id?: any }) {
|
||||
invariant(
|
||||
objA.id && objB.id,
|
||||
"Expected both fragment reference objects to have an id field"
|
||||
);
|
||||
return objA.id === objB.id;
|
||||
}
|
|
@ -1,8 +1,3 @@
|
|||
/**
|
||||
* TODO: Rewrite this to mimic Relay's preload APIs and move it to
|
||||
* @graphitation/apollo-react-relay-duct-tape.
|
||||
*/
|
||||
|
||||
import {
|
||||
useApolloClient,
|
||||
ApolloQueryResult,
|
||||
|
@ -11,6 +6,10 @@ import {
|
|||
import { DocumentNode } from "graphql";
|
||||
import { useRef, useState, useEffect } from "react";
|
||||
|
||||
/**
|
||||
* TODO: Rewrite this to mimic Relay's preload APIs and move it to
|
||||
* @graphitation/apollo-react-relay-duct-tape.
|
||||
*/
|
||||
export function useExecuteAndWatchQuery(
|
||||
executionQuery: DocumentNode,
|
||||
watchQuery: DocumentNode,
|
Загрузка…
Ссылка в новой задаче