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