Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error after updating to the latest version #233

Closed
TomlDev opened this issue Aug 30, 2019 · 4 comments
Closed

Error after updating to the latest version #233

TomlDev opened this issue Aug 30, 2019 · 4 comments

Comments

@TomlDev
Copy link

TomlDev commented Aug 30, 2019

@bkrem, I get this error after updating to the latest version:

TypeError: a is null
value
node_modules/react-d3-tree/lib/react-d3-tree.min.js:1024

  1021 | 
  1022 | if (n) {
  1023 |   var i = "object" === (void 0 === a ? "undefined" : u(a)),
> 1024 |       s = i ? a.x : 0,
       | ^  1025 |       l = i ? a.y : 0;
  1026 |   return "horizontal" === t ? "translate(" + l + "," + s + ")" : "translate(" + s + "," + l + ")";
  1027 | }

t
node_modules/react-d3-tree/lib/react-d3-tree.min.js:970

  967 | }
  968 | 
  969 | return n = r = a(this, (e = t.__proto__ || Object.getPrototypeOf(t)).call.apply(e, [this].concat(l))), r.state = {
> 970 |   transform: r.setTransform(r.props.nodeData, r.props.orientation, !0),
      | ^  971 |   initialStyle: {
  972 |     opacity: 0
  973 |   }

Originally posted by @TomlDev in #231 (comment)

@bkrem
Copy link
Owner

bkrem commented Aug 30, 2019

Hi @TomlDev,

Can you post the reproduction case for this please? At what point are you seeing this?

@TomlDev
Copy link
Author

TomlDev commented Sep 2, 2019

This is the full stracktrace:

TypeError: a is null
value
node_modules/react-d3-tree/lib/react-d3-tree.min.js:1024

  1021 | 
  1022 | if (n) {
  1023 |   var i = "object" === (void 0 === a ? "undefined" : u(a)),
> 1024 |       s = i ? a.x : 0,
       | ^  1025 |       l = i ? a.y : 0;
  1026 |   return "horizontal" === t ? "translate(" + l + "," + s + ")" : "translate(" + s + "," + l + ")";
  1027 | }

t
node_modules/react-d3-tree/lib/react-d3-tree.min.js:970

  967 | }
  968 | 
  969 | return n = r = a(this, (e = t.__proto__ || Object.getPrototypeOf(t)).call.apply(e, [this].concat(l))), r.state = {
> 970 |   transform: r.setTransform(r.props.nodeData, r.props.orientation, !0),
      | ^  971 |   initialStyle: {
  972 |     opacity: 0
  973 |   }

constructClassInstance
node_modules/react-dom/cjs/react-dom.development.js:11742

  11739 |     new ctor(props, context); // eslint-disable-line no-new
  11740 |   }
  11741 | }
> 11742 | var instance = new ctor(props, context);
        | ^  11743 | var state = workInProgress.memoizedState = instance.state !== null && instance.state !== undefined ? instance.state : null;
  11744 | adoptClassInstance(workInProgress, instance);
  11745 | {

updateClassComponent
node_modules/react-dom/cjs/react-dom.development.js:15221

  15218 |   } // In the initial pass we might need to construct the instance.
  15219 | 
  15220 | 
> 15221 |   constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
        | ^  15222 |   mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
  15223 |   shouldUpdate = true;
  15224 | } else if (current$$1 === null) {

beginWork
node_modules/react-dom/cjs/react-dom.development.js:16221

  16218 | 
  16219 |     var _resolvedProps = workInProgress.elementType === _Component2 ? _unresolvedProps : resolveDefaultProps(_Component2, _unresolvedProps);
  16220 | 
> 16221 |     return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
        | ^  16222 |   }
  16223 | 
  16224 | case HostRoot:

performUnitOfWork
node_modules/react-dom/cjs/react-dom.development.js:20241

  20238 |   startProfilerTimer(workInProgress);
  20239 | }
  20240 | 
> 20241 | next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
        | ^  20242 | workInProgress.memoizedProps = workInProgress.pendingProps;
  20243 | 
  20244 | if (workInProgress.mode & ProfileMode) {

workLoop
node_modules/react-dom/cjs/react-dom.development.js:20282

  20279 | if (!isYieldy) {
  20280 |   // Flush work without yielding
  20281 |   while (nextUnitOfWork !== null) {
> 20282 |     nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
        | ^  20283 |   }
  20284 | } else {
  20285 |   // Flush asynchronous work until there's a higher priority event

callCallback
node_modules/react-dom/cjs/react-dom.development.js:147

  144 |     window.event = windowEvent;
  145 |   }
  146 | 
> 147 |   func.apply(context, funcArgs);
      | ^  148 |   didError = false;
  149 | } // Create a global error event handler. We use this to capture the value
  150 | // that was thrown. It's possible that this error handler will fire more

invokeGuardedCallbackDev
node_modules/react-dom/cjs/react-dom.development.js:196

  193 | // errors, it will trigger our global error handler.
  194 | 
  195 | evt.initEvent(evtType, false, false);
> 196 | fakeNode.dispatchEvent(evt);
      | ^  197 | 
  198 | if (windowEventDescriptor) {
  199 |   Object.defineProperty(window, 'event', windowEventDescriptor);

invokeGuardedCallback
node_modules/react-dom/cjs/react-dom.development.js:250

  247 | function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
  248 |   hasError = false;
  249 |   caughtError = null;
> 250 |   invokeGuardedCallbackImpl$1.apply(reporter, arguments);
      | ^  251 | }
  252 | /**
  253 |  * Same as invokeGuardedCallback, but instead of returning an error, it stores

replayUnitOfWork
node_modules/react-dom/cjs/react-dom.development.js:19465

  19462 | 
  19463 | isReplayingFailedUnitOfWork = true;
  19464 | originalReplayError = thrownValue;
> 19465 | invokeGuardedCallback(null, workLoop, null, isYieldy);
        | ^  19466 | isReplayingFailedUnitOfWork = false;
  19467 | originalReplayError = null;
  19468 | 

renderRoot
node_modules/react-dom/cjs/react-dom.development.js:20395

  20392 | if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
  20393 |   if (mayReplay) {
  20394 |     var failedUnitOfWork = nextUnitOfWork;
> 20395 |     replayUnitOfWork(failedUnitOfWork, thrownValue, isYieldy);
        | ^  20396 |   }
  20397 | } // TODO: we already know this isn't true in some cases.
  20398 | // At least this shows a nicer error message until we figure out the cause.

performWorkOnRoot
node_modules/react-dom/cjs/react-dom.development.js:21319

  21316 |   cancelTimeout(timeoutHandle);
  21317 | }
  21318 | 
> 21319 | renderRoot(root, isYieldy);
        | ^  21320 | finishedWork = root.finishedWork;
  21321 | 
  21322 | if (finishedWork !== null) {

performWork
node_modules/react-dom/cjs/react-dom.development.js:21229

  21226 |   }
  21227 | } else {
  21228 |   while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime) {
> 21229 |     performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
        | ^  21230 |     findHighestPriorityRoot();
  21231 |   }
  21232 | } // We're done flushing work. Either we ran out of time in this callback,

performSyncWork
node_modules/react-dom/cjs/react-dom.development.js:21203

  21200 | }
  21201 | 
  21202 | function performSyncWork() {
> 21203 |   performWork(Sync, false);
        | ^  21204 | }
  21205 | 
  21206 | function performWork(minExpirationTime, isYieldy) {

requestWork
node_modules/react-dom/cjs/react-dom.development.js:21058

  21055 | 
  21056 | 
  21057 | if (expirationTime === Sync) {
> 21058 |   performSyncWork();
        | ^  21059 | } else {
  21060 |   scheduleCallbackWithExpirationTime(root, expirationTime);
  21061 | }

scheduleWork
node_modules/react-dom/cjs/react-dom.development.js:20871

  20868 | !isWorking || isCommitting$1 || // ...unless this is a different root than the one we're rendering.
  20869 | nextRoot !== root) {
  20870 |   var rootExpirationTime = root.expirationTime;
> 20871 |   requestWork(root, rootExpirationTime);
        | ^  20872 | }
  20873 | 
  20874 | if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {

enqueueSetState
node_modules/react-dom/cjs/react-dom.development.js:11571

  11568 | 
  11569 |   flushPassiveEffects();
  11570 |   enqueueUpdate(fiber, update);
> 11571 |   scheduleWork(fiber, expirationTime);
        | ^  11572 | },
  11573 | enqueueReplaceState: function enqueueReplaceState(inst, payload, callback) {
  11574 |   var fiber = get(inst);

./node_modules/react/cjs/react.development.js/Component.prototype.setState
node_modules/react/cjs/react.development.js:336

  333 | 
  334 | Component.prototype.setState = function (partialState, callback) {
  335 |   !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : void 0;
> 336 |   this.updater.enqueueSetState(this, partialState, callback, 'setState');
      | ^  337 | };
  338 | /**
  339 |  * Forces an update. This should only be invoked when it is known with

_callee$
src/pages/DeviceManager/Topology/DeviceTopology.tsx:91

  88 | if (response && response.tree && response.tree.length > 0) {
  89 |     const responseData = response.tree[0];
  90 |     const type = responseData.deviceType ? responseData.deviceType.name : responseData.type;
> 91 |     this.setState({
     | ^  92 |         data: {
  93 |             ...responseData,
  94 |             deviceId: responseData.id,

tryCatch
node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:62
invoke
node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:288
defineIteratorMethods/</prototype[method]
node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:114
asyncGeneratorStep
node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:3
_next
node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:25

Do you still need production code? Is it perhaps because of outdated react-dom module?

UPDATE: Hmm okay, I updated react-dom from 16.8.4 to 16.9.0 but the error persists..

This is my code:

DeviceTopology.tsx

import * as React from 'react';
// import { Tree, ReactD3TreeItem, ReactD3TreeTranslate } from 'react-d3-tree';
import Tree from 'react-d3-tree';
import { fetchDeviceGroupsTreeFromDB, removeDeviceGroupFromDB } from '../../../api/apiHelpers';
import { DeviceGroup } from "../../../models/device-group/device-group";
import { Device } from "../../../models/device/device";
import DeviceTopologyNodeLabel from "./DeviceTopologyNodeLabel";
import COUNTRY from '../../../assets/device-groups/country.svg';
import CITY from '../../../assets/device-groups/city.svg';

interface DeviceGroupTreeData extends DeviceGroup, Device {
    nodeSvgShape?: {
        shape: string,
        shapeProps: object
    }
}

export interface DeviceTopologyProps {
}

export interface DeviceTopologyState {
    translate: object;
    data: DeviceGroupTreeData;
    hoveredNode: any;
}

const deviceGroupIcons: any = {
    CITY,
    COUNTRY,
};

class DeviceTopology extends React.Component<DeviceTopologyProps, DeviceTopologyState> {
    treeContainer: HTMLDivElement | null;
    constructor(props: DeviceTopologyProps) {
        super(props);
        this.state = {
            translate: { x: 0, y: 0 },
            data: {},
            hoveredNode: {},
        };
    }

    componentDidMount = async () => {
        if (this.treeContainer) {
            const dimensions = this.treeContainer.getBoundingClientRect();
            this.setState({
                translate: {
                    x: dimensions.width / 2,
                    y: 100
                }
            });
        }
        const response = await fetchDeviceGroupsTreeFromDB();
        if (response && response.tree && response.tree.length > 0) {
            const responseData = response.tree[0];
            const type = responseData.deviceType ? responseData.deviceType.name : responseData.type;
            this.setState({
                data: {
                    ...responseData,
                    deviceId: responseData.id,
                    nodeSvgShape: this.getSvgShapeForType(type),
                    children: responseData.children
                        ? this.getChildrensWithIcon(responseData.children)
                        : undefined,
                },
            });
        }
    };

    removeDeviceGroup = async (id: string, refresh: boolean = true) => {
        const response = await removeDeviceGroupFromDB(id);

        if (refresh && response && response.status === 200) {
            await fetchDeviceGroupsTreeFromDB();
        }
    };

    getSvgShapeForType = (type: string | undefined) => {
        return {
            shape: 'image',
            shapeProps: {
                href: type ? deviceGroupIcons[type] : QUESTION_MARK,
                width: 60,
                height: 60,
                x: -30,
                y: -80
            }
        };
    };

    getChildrensWithIcon = (children: DeviceGroupTreeData[]) => {
        for (const [index, node] of children.entries()) {
            node.deviceId = node.id
            const type = node.deviceType ? node.deviceType.name : node.type;
            children[index].nodeSvgShape = this.getSvgShapeForType(type);
            if (node.children) {
                // @ts-ignore
                children.children = this.getChildrensWithIcon(node.children)
            }
        }
        return children;
    };

    render() {
        return (
            <div id="treeWrapper" style={{ width: '100%', height: '100%' }} ref={tc => (this.treeContainer = tc)}>
                <Tree
                    data={this.state.data}
                    translate={this.state.translate}
                    orientation='vertical'
                    allowForeignObjects={true}
                    nodeLabelComponent={{
                        render: <DeviceTopologyNodeLabel
                            nodeData={this.state.data}
                            hoveredNode={this.state.hoveredNode}
                        />,
                        foreignObjectWrapper: {
                            y: -23,
                            width: 70,
                            height: 50,
                        }
                    }}
                    onMouseOver={(nodeData) => { this.setState({ hoveredNode: nodeData }) }}
                    styles={{
                        links: {
                            stroke: '#00376c',
                            strokeWidth: 0.3,
                        }
                    }}
                />
            </div>
        );
    }
}

export default DeviceTopology;

DeviceTopologyNodeLabel.tsx

import { faCog, faAngleDoubleDown } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import * as React from 'react';
import TableMenu from '../Table/TableMenu';


export interface DeviceTopologyNodeLabelProps {
    nodeData: any;
    hoveredNode: any;
}

export interface DeviceTopologyNodeLabelState {
    showMenu: boolean;
    nodeLabelNameOffset: number;
    boundingClientRect: ClientRect;
}

class DeviceTopologyNodeLabel extends React.Component<
    DeviceTopologyNodeLabelProps,
    DeviceTopologyNodeLabelState
    > {
    nodeLabelName: HTMLSpanElement | null;
    state = {
        showMenu: false,
        nodeLabelNameOffset: 0,
        boundingClientRect: { x: 0, y: 0, width: 0, height: 0, top: 0, right: 0, bottom: 0, left: 0 },
    };

    componentDidMount = async () => {
        if (this.nodeLabelName) {
            await this.getBoundingClientRect();
            if (this.state.boundingClientRect.width > 140) {
                this.truncateName(this.state.boundingClientRect.width);
            }
            this.setState({
                nodeLabelNameOffset: - this.state.boundingClientRect.width / 2
            });
        };
    }

    getBoundingClientRect = () => {
        if (this.nodeLabelName) {
            this.setState({
                boundingClientRect: this.nodeLabelName.getBoundingClientRect()
            })
        }
    }

    truncateName = (width: number) => {
        const name = this.props.nodeData.name;
        const charsToShow = 126 / (width / name.length);        // (maximum pixels / average charWidth)
        const frontChars = Math.floor(charsToShow / 2) + 2;
        const backChars = Math.floor(charsToShow / 2) - 2;
        this.props.nodeData.name = `${name.substr(0, frontChars)}...${name.substr(name.length - backChars)}`;
    }

    toggleMenu = async () => {
        await this.getBoundingClientRect();
        this.setState((prevState) => ({ showMenu: !prevState.showMenu }));
    };

    render() {
        const { nodeData } = this.props;
        return (
            <span>
                <span
                    className='tree__nodeLabel'
                    ref={tc => (this.nodeLabelName = tc)}
                    style={{ marginLeft: this.state.nodeLabelNameOffset }}
                >{nodeData.name}</span>
                <div className='tree__nodeLabel__icons'>
                    {nodeData._children && nodeData._collapsed ?
                        <span className='tree__nodeLabel__icon tree__nodeLabel__icon__expand'>
                            <FontAwesomeIcon icon={faAngleDoubleDown} />
                        </span> :
                        ''}
                    {!nodeData.hasOwnProperty("type") ? // is device-group or device
                        <span
                            className='tree__nodeLabel__icon tree__nodeLabel__icon__menu'
                            onClick={this.toggleMenu}
                        >
                            <FontAwesomeIcon icon={faCog} />
                        </span> :
                            ''}
                </div>
                <div>
                    {this.state.showMenu &&
                        <TableMenu                        />
                    }
                </div>
            </span>
        );
    }
}

export default DeviceTopologyNodeLabel;

@TomlDev
Copy link
Author

TomlDev commented Sep 5, 2019

The error was not giving any clues. Before we were destructuring everything at the root of responseData which also included keys like metadata which react-d3-tree doesn't need at all. Got it fixed, was problem on my side, thanks.

@TomlDev TomlDev closed this as completed Sep 5, 2019
@bkrem
Copy link
Owner

bkrem commented Sep 6, 2019

Yeah my original assumption was that you had an issue with the TS compiler after I updated the definitions (since that's all that changed), but since this seems to be a runtime error it can't be that.

I realise that the minified-by-default situation is annoying (one of multiple regrets I have after-the-fact for v1).

With the limited time I can still spend on this repo I'm considering shipping a v2 with only the most essential breaking changes (update legacy react code, migrate to TS for proper typing across the board, provide unminifed lib resources and more options than just UMD), and delaying the API refactor (the Tree API is way too opinionated in v1) to v3 which would then hopefully resolve a lot of individual feature requests when people can handle their individual use cases via props.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants