Skip to content

Some child renders, sometimes they just dont #26

@maifeeulasad

Description

@maifeeulasad

I've really simple code to render a Tree :

import React from 'react';
import Tree from "./components/Tree";

class App extends React.Component{

    treeRef = React.createRef();

    state={
        data : {
            ...
        }
    }

    onUpdate = (node) => {
        console.debug(node)
        this.setState({ node: node });
    };

    render() {
        return(
            <div>
                <Tree
                    ref={this.treeRef}
                    onUpdate={this.onUpdate}
                    data={this.state.data}
                />
            </div>
        )
    }
}

export default App;

But when I try to render this data :

{
            id: 'fruit',
            name: 'Fruit',
            children: [{
                id: 'apple',
                name: 'Apple'
            },{
                id: 'banana',
                name: 'Banana',
                children: [{
                    id: 'cherry',
                    name: 'Cherry'
                }]
            }]
        }

Screenshot from 2020-11-04 21-06-24

And when trying to render this data :

{
            id: 'fruit',
            name: 'Fruit',
            children: [{
                id: 'apple',
                name: 'Apple'
            }{
                id: 'komola',
                name: 'komola'
            },{
                id: 'banana',
                name: 'Banana',
                children: [{
                    id: 'cherry',
                    name: 'Cherry'
                }]
            }]
        }

Screenshot from 2020-11-04 21-07-02

When changing the tree structure some child renders, some doesn't

And Tree.js is almost as it is in example, still here is the code

Expand :
import Checkbox from '@trendmicro/react-checkbox';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import React, { Component } from 'react';
import InfiniteTree from 'react-infinite-tree';

const renderTreeNode = ({ node, tree, toggleState, onUpdate }) => (
    <TreeNode
        selected={node.state.selected}
        depth={node.state.depth}
        onClick={(event) => {
            tree.selectNode(node);
        }}
    >
        <Toggler
            state={toggleState}
            onClick={(event) => {
                event.stopPropagation();

                if (toggleState === 'closed') {
                    tree.openNode(node);
                } else if (toggleState === 'opened') {
                    tree.closeNode(node);
                }
            }}
        />
        <Checkbox
            checked={node.state.checked}
            indeterminate={node.state.indeterminate}
            onClick={(event) => {
                event.stopPropagation();
            }}
            onChange={(event) => {
                tree.checkNode(node);
                onUpdate(node);
            }}
        />
        <Clickable>
            <Icon state={toggleState} />
            <Text>{node.name}</Text>
        </Clickable>
        {(node.loadOnDemand && node.children.length === 0 && !node.state.loading) &&
        <i className="fa fa-fw fa-ellipsis-v" />
        }
        {node.state.loading && <Loading />}
    </TreeNode>
);

class Tree extends Component {
    static propTypes = {
        onUpdate: PropTypes.func
    };

    treeRef = React.createRef();
    tree = null;

    componentDidMount() {
        const { tree } = this.treeRef.current;

        // Select the first node
        tree.selectNode(tree.getChildNodes()[0]);
    }

    render() {
        console.debug(this.props.data)
        const { onUpdate } = this.props;

        if (this.treeRef.current) {
            this.tree = this.treeRef.current.tree;
        }

        return (
            <InfiniteTree
                ref={this.treeRef}
                style={{
                    border: '1px solid #ccc'
                }}
                autoOpen
                selectable
                tabIndex={0}
                data={this.props.data}
                height="100%"
                width={400}
                rowHeight={30}
                shouldLoadNodes={(node) => {
                    return !node.hasChildren() && node.loadOnDemand;
                }}
                loadNodes={(parentNode, done) => {
                    const suffix = parentNode.id.replace(/(\w)+/, '');
                    const nodes = [
                        {
                            id: 'node1' + suffix,
                            name: 'Node 1'
                        },
                        {
                            id: 'node2' + suffix,
                            name: 'Node 2'
                        }
                    ];
                    setTimeout(() => {
                        done(null, nodes);
                    }, 1000);
                }}
                shouldSelectNode={(node) => { // Defaults to null
                    const { tree } = this.treeRef.current;

                    if (!node || (node === tree.getSelectedNode())) {
                        return false; // Prevent from deselecting the current node
                    }
                    return true;
                }}
                onKeyUp={(event) => {
                    console.log('onKeyUp', event.target);
                }}
                onKeyDown={(event) => {
                    const { tree } = this.treeRef.current;

                    console.log('onKeyDown', event.target);

                    event.preventDefault();

                    const node = tree.getSelectedNode();
                    const nodeIndex = tree.getSelectedIndex();

                    if (event.keyCode === 37) { // Left
                        tree.closeNode(node);
                    } else if (event.keyCode === 38) { // Up
                        const prevNode = tree.nodes[nodeIndex - 1] || node;
                        tree.selectNode(prevNode);
                    } else if (event.keyCode === 39) { // Right
                        tree.openNode(node);
                    } else if (event.keyCode === 40) { // Down
                        const nextNode = tree.nodes[nodeIndex + 1] || node;
                        tree.selectNode(nextNode);
                    }
                }}
                onScroll={(scrollOffset, event) => {
                    const child = event.target.firstChild;
                    const treeViewportHeight = 400;
                    console.log((scrollOffset / (child.scrollHeight - treeViewportHeight) * 100).toFixed(2));
                    console.log('onScroll', scrollOffset, event);
                }}
                onContentWillUpdate={() => {
                    console.log('onContentWillUpdate');
                }}
                onContentDidUpdate={() => {
                    const { tree } = this.treeRef.current;

                    console.log('onContentDidUpdate');
                    onUpdate(tree.getSelectedNode());
                }}
                onOpenNode={(node) => {
                    console.log('onOpenNode:', node);
                }}
                onCloseNode={(node) => {
                    console.log('onCloseNode:', node);
                }}
                onSelectNode={(node) => {
                    console.log('onSelectNode:', node);
                    onUpdate(node);
                }}
                onWillOpenNode={(node) => {
                    console.log('onWillOpenNode:', node);
                }}
                onWillCloseNode={(node) => {
                    console.log('onWillCloseNode:', node);
                }}
                onWillSelectNode={(node) => {
                    console.log('onWillSelectNode:', node);
                }}
            >
                {({ node, tree }) => {
                    const hasChildren = node.hasChildren();

                    let toggleState = '';
                    if ((!hasChildren && node.loadOnDemand) || (hasChildren && !node.state.open)) {
                        toggleState = 'closed';
                    }
                    if (hasChildren && node.state.open) {
                        toggleState = 'opened';
                    }

                    return renderTreeNode({ node, tree, toggleState, onUpdate });
                }}
            </InfiniteTree>
        );
    }
}

export default Tree;




const defaultRowHeight = 30;

const TreeNode = styled.div`
    cursor: default;
    position: relative;
    line-height: ${({ rowHeight = defaultRowHeight }) => rowHeight - 2}px;
    background: ${props => props.selected ? '#deecfd' : 'transparent'};
    border: ${props => props.selected ? '1px solid #06c' : '1px solid transparent'};
    padding-left: ${props => props.depth * 18}px;
    .dropdown {
        visibility: hidden;
    }
    &:hover {
        background: #f2fdff;
        .dropdown {
            visibility: inherit;
        }
    }
`;

const Icon = ({ state, ...props }) => (
    <span {...props}>
    {{
        'opened': (<i className="fa fa-fw fa-folder-open-o" />),
        'closed': (<i className="fa fa-fw fa-folder-o" />),
    }[state] || (<i className="fa fa-fw fa-file-o" />)}
    </span>
);

const Toggler = styled(({ state, ...props }) => (
    <a {...props}>
        {(state === 'closed') &&
        <i className="fa fa-fw fa-chevron-right" />
        }
        {(state === 'opened') &&
        <i className="fa fa-fw fa-chevron-down" />
        }
    </a>
))`
    color: #333;
    display: inline-block;
    text-align: center;
    margin-right: 2px;
`;


const Clickable = styled.div`
    display: inline-block;
    cursor: pointer;
`;


const Text = styled.span`
    margin-left: 0 2px;
    user-select: none;
`;

const Loading = () => (
    <i className="fa fa-fw fa-spin fa-spinner" />
);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions