mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-22 22:19:48 +03:00
implement solution using useRef
This commit is contained in:
parent
0680165deb
commit
4574eb0c62
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
import ItemRange from './ItemRange';
|
import ItemRange from './ItemRange';
|
||||||
import JSONArrow from './JSONArrow';
|
import JSONArrow from './JSONArrow';
|
||||||
import JSONNode from './JSONNode';
|
import JSONNode from './JSONNode';
|
||||||
|
@ -113,47 +113,52 @@ export default function JSONNestedNode(props: Props) {
|
||||||
shouldExpandNodeInitially,
|
shouldExpandNodeInitially,
|
||||||
styling,
|
styling,
|
||||||
} = props;
|
} = props;
|
||||||
|
const { shouldExpandNode, setEnableDefaultButton, setShouldExpandNode } =
|
||||||
const { shouldExpandNode, setShouldExpandNode, setEnableDefaultButton } =
|
|
||||||
useExpandableButtonContext();
|
useExpandableButtonContext();
|
||||||
|
|
||||||
const [expanded, setExpanded] = useState<boolean>(
|
const [defaultExpanded] = useState<boolean>(
|
||||||
// calculate individual node expansion if necessary
|
// calculate individual node expansion if necessary
|
||||||
isCircular ? false : shouldExpandNodeInitially(keyPath, data, level)
|
isCircular
|
||||||
|
? false
|
||||||
|
: (function getDefault(){
|
||||||
|
switch (shouldExpandNode) {
|
||||||
|
case 'expand': return true;
|
||||||
|
case 'collapse': return false;
|
||||||
|
default: return shouldExpandNodeInitially(keyPath, data, level);
|
||||||
|
}
|
||||||
|
})()
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultExpanded = shouldExpandNodeInitially(keyPath, data, level);
|
const [, setTriggerReRender] = useState<boolean>(defaultExpanded);
|
||||||
|
|
||||||
useEffect(() => {
|
/**
|
||||||
switch (shouldExpandNode) {
|
* Used the useRef to handle expanded because calling a setState in a recursive implementation
|
||||||
case 'expand':
|
* could lead to a "Maximum update depth exceeded" error */
|
||||||
setExpanded(true);
|
const expandedRef = useRef<boolean>(defaultExpanded)
|
||||||
break;
|
|
||||||
case 'collapse':
|
switch (shouldExpandNode) {
|
||||||
setExpanded(false);
|
case 'expand': expandedRef.current = isCircular ? false : true; break;
|
||||||
break;
|
case 'collapse': expandedRef.current = false; break;
|
||||||
case 'default':
|
case 'default': expandedRef.current = defaultExpanded; break;
|
||||||
setExpanded(defaultExpanded);
|
default: //Do nothing;
|
||||||
break;
|
}
|
||||||
default: // Do nothing
|
|
||||||
}
|
|
||||||
}, [defaultExpanded, shouldExpandNode]);
|
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
if (expandable) {
|
if (expandable) {
|
||||||
setExpanded(!expanded);
|
expandedRef.current = !expandedRef.current
|
||||||
|
setTriggerReRender((e) => !e)
|
||||||
setEnableDefaultButton(true);
|
setEnableDefaultButton(true);
|
||||||
setShouldExpandNode(undefined);
|
setShouldExpandNode(undefined)
|
||||||
}
|
}
|
||||||
}, [expandable, expanded, setEnableDefaultButton, setShouldExpandNode]);
|
}, [expandable, setEnableDefaultButton, setShouldExpandNode]);
|
||||||
|
|
||||||
const renderedChildren =
|
const renderedChildren =
|
||||||
expanded || (hideRoot && level === 0)
|
expandedRef.current || (hideRoot && level === 0)
|
||||||
? renderChildNodes({ ...props, circularCache, level: level + 1 })
|
? renderChildNodes({ ...props, circularCache, level: level + 1 })
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const itemType = (
|
const itemType = (
|
||||||
<span {...styling('nestedNodeItemType', expanded)}>
|
<span {...styling('nestedNodeItemType', expandedRef.current)}>
|
||||||
{nodeTypeIndicator}
|
{nodeTypeIndicator}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
@ -164,7 +169,7 @@ export default function JSONNestedNode(props: Props) {
|
||||||
createItemString(data, collectionLimit),
|
createItemString(data, collectionLimit),
|
||||||
keyPath
|
keyPath
|
||||||
);
|
);
|
||||||
const stylingArgs = [keyPath, nodeType, expanded, expandable] as const;
|
const stylingArgs = [keyPath, nodeType, expandedRef.current, expandable] as const;
|
||||||
|
|
||||||
return hideRoot ? (
|
return hideRoot ? (
|
||||||
<li {...styling('rootNode', ...stylingArgs)}>
|
<li {...styling('rootNode', ...stylingArgs)}>
|
||||||
|
@ -178,7 +183,7 @@ export default function JSONNestedNode(props: Props) {
|
||||||
<JSONArrow
|
<JSONArrow
|
||||||
styling={styling}
|
styling={styling}
|
||||||
nodeType={nodeType}
|
nodeType={nodeType}
|
||||||
expanded={expanded}
|
expanded={expandedRef.current}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -59,6 +59,19 @@ const getDefaultThemeStyling = (theme: Base16Theme): StylingConfig => {
|
||||||
backgroundColor: colors.BACKGROUND_COLOR,
|
backgroundColor: colors.BACKGROUND_COLOR,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
expandable: {
|
||||||
|
color: colors.TEXT_COLOR,
|
||||||
|
backgroundColor: colors.BACKGROUND_COLOR,
|
||||||
|
position: 'absolute',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '1rem',
|
||||||
|
top: '1rem',
|
||||||
|
right: '1rem',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
|
||||||
value: ({ style }, nodeType, keyPath) => ({
|
value: ({ style }, nodeType, keyPath) => ({
|
||||||
style: {
|
style: {
|
||||||
...style,
|
...style,
|
||||||
|
|
|
@ -7,9 +7,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import { Expandable } from '.';
|
import { Expandable } from '.';
|
||||||
import { useExpandableButtonContext } from './expandableButtonsContext';
|
import { useExpandableButtonContext } from './expandableButtonsContext';
|
||||||
|
import { StylingFunction } from 'react-base16-styling';
|
||||||
|
|
||||||
interface ExpandableButtonsProps {
|
interface ExpandableButtonsProps {
|
||||||
expandable: Expandable;
|
expandable: Expandable;
|
||||||
|
styling: StylingFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExpandButtonProps {
|
interface ExpandButtonProps {
|
||||||
|
@ -26,24 +28,13 @@ interface DefaultButtonProps {
|
||||||
defaultIcon?: ReactNode;
|
defaultIcon?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ExpandableButtons({ expandable }: ExpandableButtonsProps) {
|
function ExpandableButtons({ expandable, styling }: ExpandableButtonsProps) {
|
||||||
const { enableDefaultButton } = useExpandableButtonContext();
|
const { enableDefaultButton } = useExpandableButtonContext();
|
||||||
|
|
||||||
const expandableDefaultValue = expandable?.defaultValue || 'expand';
|
const expandableDefaultValue = expandable?.defaultValue || 'expand';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div {...styling('expandable')}>
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '1rem',
|
|
||||||
top: '1rem',
|
|
||||||
right: '1rem',
|
|
||||||
cursor: 'pointer',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{enableDefaultButton && (
|
{enableDefaultButton && (
|
||||||
<DefaultButton defaultIcon={expandable?.defaultIcon} />
|
<DefaultButton defaultIcon={expandable?.defaultIcon} />
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import React, {
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Expandable } from '.';
|
import { Expandable } from '.';
|
||||||
import ExpandableButtons from './expandableButtons';
|
import ExpandableButtons from './expandableButtons';
|
||||||
|
import { StylingFunction } from 'react-base16-styling';
|
||||||
|
|
||||||
interface Context {
|
interface Context {
|
||||||
enableDefaultButton: boolean;
|
enableDefaultButton: boolean;
|
||||||
|
@ -18,11 +19,12 @@ interface Context {
|
||||||
interface Props {
|
interface Props {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
expandable?: Expandable;
|
expandable?: Expandable;
|
||||||
|
styling: StylingFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExpandableButtonsContext = createContext<Context>({} as Context);
|
const ExpandableButtonsContext = createContext<Context>({} as Context);
|
||||||
|
|
||||||
function ExpandableButtonsContextProvider({ expandable, children }: Props) {
|
function ExpandableButtonsContextProvider({ expandable, children, styling }: Props) {
|
||||||
const [enableDefaultButton, setEnableDefaultButton] = useState(false);
|
const [enableDefaultButton, setEnableDefaultButton] = useState(false);
|
||||||
const [shouldExpandNode, setShouldExpandNode] = useState();
|
const [shouldExpandNode, setShouldExpandNode] = useState();
|
||||||
|
|
||||||
|
@ -39,7 +41,7 @@ function ExpandableButtonsContextProvider({ expandable, children }: Props) {
|
||||||
return (
|
return (
|
||||||
<ExpandableButtonsContext.Provider value={value}>
|
<ExpandableButtonsContext.Provider value={value}>
|
||||||
{children}
|
{children}
|
||||||
{expandable && <ExpandableButtons expandable={expandable} />}
|
{expandable && <ExpandableButtons expandable={expandable} styling={styling} />}
|
||||||
</ExpandableButtonsContext.Provider>
|
</ExpandableButtonsContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ export function JSONTree({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul {...styling('tree')}>
|
<ul {...styling('tree')}>
|
||||||
<ExpandableButtonsContext expandable={expandable}>
|
<ExpandableButtonsContext expandable={expandable} styling={styling}>
|
||||||
<JSONNode
|
<JSONNode
|
||||||
keyPath={hideRoot ? [] : keyPath}
|
keyPath={hideRoot ? [] : keyPath}
|
||||||
value={postprocessValue(value)}
|
value={postprocessValue(value)}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { createRenderer } from 'react-test-renderer/shallow';
|
import { createRenderer } from 'react-test-renderer/shallow';
|
||||||
|
|
||||||
import { JSONTree } from '../src/index';
|
import { JSONTree } from '../src/index';
|
||||||
import JSONNode from '../src/JSONNode';
|
import ExpandableButtonsContextProvider from '../src/expandableButtonsContext';
|
||||||
|
|
||||||
const BASIC_DATA = { a: 1, b: 'c' };
|
const BASIC_DATA = { a: 1, b: 'c' };
|
||||||
|
|
||||||
|
@ -17,6 +17,6 @@ describe('JSONTree', () => {
|
||||||
const result = render(<JSONTree data={BASIC_DATA} />);
|
const result = render(<JSONTree data={BASIC_DATA} />);
|
||||||
|
|
||||||
expect(result.type).toBe('ul');
|
expect(result.type).toBe('ul');
|
||||||
expect(result.props.children[0].type.name).toBe(JSONNode.name);
|
expect(result.props.children.type.name).toBe(ExpandableButtonsContextProvider.name);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user