Handling Tree structured state in redux using recursive components

State

const INTIAL_STATE = {
filterText: '',
tree: [
{
"node": {
"id": '1',
"description": 'test1',
"children": [
{
"node": {
id:'1_1',
"description": 'test1_1',
"children": [
{
"node": {
id: '1_1_1',
"description": 'test1_1_1',
"children": []
}
}
]
}
},
{
"node": {
id:'1_2',
"description": 'test1_2',
"children": []
}
},
{
"node": {
id:'1_3',
"description": 'test1_3',
"children": []
}
}
]
}
},
{
"node": {
"id": '2',
"description": 'test2',
"children": [
{
"node": {
id:'2_1',
"description": 'test2_1',
"children": [
{
"node": {
id: '2_1_1',
"description": 'test2_1_1',
"children": []
}
}
]
}
},
{
"node": {
id:'2_2',
"description": 'test2_2',
"children": []
}
},
{
"node": {
id:'2_3',
"description": 'test2_3',
"children": []
}
}
]
}
}
]
}

REDUCER

export default function (state = INTIAL_STATE, action) {
switch(action.type){
case 'FILTER_TREEVIEW': {
return Object.assign({}, state, {filterText: action.payload})
}
default: return state;
}
}

ELEMENT COMPONENT

import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {filter} from '../actions/index';
//comment
class Element extends React.Component {
render () {
return (
<li>
{this.props.element.node.description}
{
this.props.element.node.children.length > 0 &&
this.props.element.node.children.filter(child => child.node.description.match(this.props.filterText) || this.props.filterText.match(child.node.description)).length > 0 && (
<ul>
{
this.props.element.node.children.filter(child => child.node.description.match(this.props.filterText) || this.props.filterText.match(child.node.description))
.map((child, idx) => <Element key={idx} element={child} />)
}
</ul>
)}
</li>
);
}
}
Element.propTypes = {
element: PropTypes.object.isRequired
}
function mapStateToProps(state, ownProps) {
return {
filterText: state.filtering.filterText
}
}
export default connect(mapStateToProps, {filter})(Element);

PARENT COMPONENT

import React from 'react';
import {connect} from 'react-redux';
import {filter} from '../actions/index';
import Element from './Element';
class Assignment extends React.Component {
constructor(props) {
super(props);
this.state = {
searchTerm: ''
}
this.handleSearchChange = this.handleSearchChange.bind(this);
}
handleSearchChange(e) {
this.setState({
searchTerm: e.target.value
});
}
componentDidMount () {
this.props.filter('')
}
render () {
return (
<div>
<input type='text' name='searchBox' placeholder='search' onChange={this.handleSearchChange}/>
<button onClick={(e) => this.props.filter(this.state.searchTerm)}>Search</button>
<hr />
<ul>
{this.props.filteredTree.length > 0 && (
this.props.filteredTree.map((ele, idx) => <Element key={idx} element={ele} />)
)}
</ul>
</div>
);
}
}
function mapStateToProps (state) {
return {
filteredTree: state.filtering.tree.filter(ele => ele.node.description.match(state.filtering.filterText) || state.filtering.filterText.match(ele.node.description))
}
}
export default connect(mapStateToProps, {
filter
})(Assignment);

In the Parent component, we get the state from redux and then filter the tree component of state to use only trees whose description matches the filter text.

Then we map over the filtered tree and for each node, return a ELEMENT component and pass it the node as props.

In the ELEMENT component, we check if the element has children and if so, filter the children based on filter text and map over the filtered children, and return an ELEMENT component for each child. This continues until the element has no children.

Like what you read? Give Aditya Ps a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.