Skip to content

Commit

Permalink
fix: Improve stability of initial state sync in Snaps UI
Browse files Browse the repository at this point in the history
  • Loading branch information
FrederikBolding committed Jun 18, 2024
1 parent 3820f01 commit 7d074a0
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 13 deletions.
20 changes: 16 additions & 4 deletions ui/components/app/snaps/snap-ui-renderer/snap-ui-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import MetaMaskTemplateRenderer from '../../metamask-template-renderer/metamask-
import { SnapDelineator } from '../snap-delineator';
import {
getSnapMetadata,
getMemoizedInterfaceContent,
getMemoizedInterface,
} from '../../../../selectors';
import { Box, FormTextField } from '../../../component-library';
import { DelineatorType } from '../../../../helpers/constants/snaps';
Expand Down Expand Up @@ -35,8 +35,15 @@ const SnapUIRendererComponent = ({
getSnapMetadata(state, snapId),
);

const content = useSelector((state) =>
getMemoizedInterfaceContent(state, interfaceId),
const {
state: initialState,
context,
content,
} = useSelector(
(state) => getMemoizedInterface(state, interfaceId),
// We only want to update the state if the content has changed.
// We do this to avoid useless re-renders.
(oldState, newState) => isEqual(oldState.content, newState.content),
);

// sections are memoized to avoid useless re-renders if one of the parents element re-renders.
Expand Down Expand Up @@ -74,7 +81,12 @@ const SnapUIRendererComponent = ({
boxProps={boxProps}
>
<Box className="snap-ui-renderer__content">
<SnapInterfaceContextProvider snapId={snapId} interfaceId={interfaceId}>
<SnapInterfaceContextProvider
snapId={snapId}
interfaceId={interfaceId}
initialState={initialState}
context={context}
>
<MetaMaskTemplateRenderer sections={sections} />
</SnapInterfaceContextProvider>
{isPrompt && (
Expand Down
16 changes: 7 additions & 9 deletions ui/contexts/snaps/snap-interface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
InterfaceState,
UserInputEventType,
} from '@metamask/snaps-sdk';
import { Json } from '@metamask/utils';
import { debounce, throttle } from 'lodash';
import React, {
FunctionComponent,
Expand All @@ -11,8 +12,7 @@ import React, {
useEffect,
useRef,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getMemoizedInterface } from '../../selectors';
import { useDispatch } from 'react-redux';
import {
handleSnapRequest,
updateInterfaceState,
Expand Down Expand Up @@ -47,6 +47,8 @@ export const SnapInterfaceContext =
export type SnapInterfaceContextProviderProps = {
interfaceId: string;
snapId: string;
initialState: Record<string, string | Record<string, unknown> | unknown>;
context: Json;
};

// We want button clicks to be instant and therefore use throttling
Expand All @@ -64,18 +66,14 @@ const THROTTLED_EVENTS = [
* @param params.children - The childrens to wrap with the context provider.
* @param params.interfaceId - The interface ID to use.
* @param params.snapId - The Snap ID that requested the interface.
* @param params.initialState - The initial state of the interface.
* @param params.context - The context blob of the interface.
* @returns The context provider.
*/
export const SnapInterfaceContextProvider: FunctionComponent<
SnapInterfaceContextProviderProps
> = ({ children, interfaceId, snapId }) => {
> = ({ children, interfaceId, snapId, initialState, context }) => {
const dispatch = useDispatch();
const { state: initialState, context } = useSelector(
(state) => getMemoizedInterface(state, interfaceId),
// Prevents the selector update.
// We do this to avoid useless re-renders.
() => true,
);

// We keep an internal copy of the state to speed-up the state update in the UI.
// It's kept in a ref to avoid useless re-rendering of the entire tree of components.
Expand Down

0 comments on commit 7d074a0

Please sign in to comment.