React: why state or props null on page refresh?

Viewed 207

I am displaying a tree view on left frame of the page. Tree is generating from xml file. On the click of each node, components are opening in the right frame of the page. ProductsTreeView is the tree component, Add_Category is the component that will open on the click of one of the tree node. I am passing the props through routing. everything is working fine as long as the page not refresh. In case of page refresh, props is showing null in the Add_Category page. Please help how to fix this. [1]: https://i.stack.imgur.com/QeYB6.gif

App.js

import React, { Component } from 'react';
import { Switch, Route, BrowserRouter, Redirect } from "react-router-dom";
import Home from './components/Home';

 export class App extends Component {
    render() {
        return (
            <div>
                <BrowserRouter>
                    <Switch>
                        <Route path="/" component={Home} />
                        <Redirect to="/" />
                    </Switch>
                </BrowserRouter>
             </div>
        );
    }
}
export default App;
_____________
 
Home.js

import React from 'react';
import { Route} from "react-router-dom";
import ProductsTree from '.ProductsTreeView';
import AddCategory from './Add_Category';

class Home extends React.Component 
    constructor(props) {
        super(props);
        this.state =
        {
            currentNode: {},
            data: "",
        };
        this.setCurrentNode = this.setCurrentNode.bind(this);
    }

    setCurrentNode(node) {
        this.setState({ currentNode: node });
    }

    render() {
        return (
            <div>
<table className="Container">

<tbody><tr width="100%">

<td className="TreeContainer">

   <ProductsTree setCurrentNode={this.setCurrentNode} /> </td>

<td className="BodyContainer">

                                             <Route path="/Add_Category">

                                                    <AddCategory key_id={this.state.currentNode.key_id} />

                                                </Route>

                            </td>   </tr>        </tbody>    </table>

            </div>

        );

    }

}

export default Home;
_________________________

***Add_Category***
 
import React from 'react'

export class Add_Category extends React.Component {

       constructor(props) {

             super(props);

             this.state = {

                    ID: "",

                    Name: "",

             };

       }

       componentDidMount() {
         if (typeof this.props.key_id !== 'undefined') {
             const ID= this.props.key_id;
             this.getName(ID);
          }
       }

       componentDidUpdate(prevProps) {

             if (prevProps.key_id !== this.props.key_id) {

                    console.log(`key_id: ${this.props.key_id}`);
                    const ID= this.props.key_id;
                    this.getName(ID);

             }

       }
       async getName(ID) {
                   await fetch(REQUEST_URL)
                           .then(response => response.json())
                           .then((data) => {
                                 this.setState({
                                       Name: data,
                                      ID: this.props.key_id,
                                        loading: false})
                                 console.log(this.state.Name)
                           })[![enter image description here][1]][1]             
       }
      render() {
        return (
                    <div>
                          <form>
       {this.state.Name}

                                </form>

                           </div>

 

        );

    }

}

export default Add_Category;

_________________________
ProductsTreeView.js
import React, { Component } from 'react';

import ReactHtmlParser from 'react-html-parser';

import axios from 'axios';

import XMLParser from 'react-xml-parser';

import { Link } from "react-router-dom";

 

class ProductsTreeView extends Component {

    render() {

        return (

            <div id="TreeView">

                <TreeView setCurrentNode={this.props.setCurrentNode} />

            </div>

        );

    }

}

 

class Node {

    description = 'n/a';

    id = -1;

    key_id = -1;

    linkpagename = '';

    isActive = false;

    nodes = [];

 

    constructor(description, id, key_id, linkpagename) {

        this.description = description;

        this.id = id;

        this.key_id = key_id;

        this.linkpagename = linkpagename;

    }

 

    static nodesFromXml(xml) {

        const map = (entity, nodes) => {

            const id = entity.attributes['id'];

            const key_id = entity.attributes['key-id'];

            const descriptionText =

                entity.children[

                    entity.children.findIndex((child) => child.name === 'description')

                ].value;

            const entities = entity.children.filter(

                (child) => child.name === 'entity'

            );

            var linkPageName = entity.attributes['link-page-name'];

            linkPageName = linkPageName.replace(".aspx", "");

            const node = new Node(descriptionText, id, key_id, linkPageName);

            nodes.push(node);

            entities.forEach((entity) => map(entity, node.nodes));

        };

 

        const parsedData = new XMLParser().parseFromString(xml);

 

        const entities = parsedData.children.filter(

            (child) => child.name === 'entity'

        );

 

        const nodes = [];

        entities.forEach((entity) => map(entity, nodes));

        return nodes;

    }

}

 

class TreeView extends React.Component {

    constructor(props) {

        super(props);

        this.state = { nodes: [] };

        this.toggleNode = this.toggleNode.bind(this);

    }

 

    componentDidMount() {

        axios

            .get(REQUEST_URL, { 'Content-Type': 'application/xml; charset=utf-8' })

            .then((response) =>

                this.setState({ nodes: Node.nodesFromXml(response.data) }))

                    .catch(function (error) {

                        if (error.response) {

                            // Request made and server responded

                            console.log(error.response.data);

                            console.log(error.response.status);

                            console.log(error.response.headers);

                        } else if (error.request) {

                            // The request was made but no response was received

                            console.log(error.request);

                        } else {

                            // Something happened in setting up the request that triggered an Error

                            console.log('Error', error.message);

                        }

 

                    });

    }

 

    render() {

        const nodes = this.state.nodes;

 

        return (

            <ul>

                 {nodes.map((node) => (

                    <TreeNode

                        id={node.id}

                        key={node.key_id}

                        node={node}

                        onToggle={this.toggleNode}

                        setCurrentNode={this.props.setCurrentNode}

                    />

                ))}

            </ul>

        );

    }

 

    toggleNode(node) {

        this.props.setCurrentNode(node);

 

        function _toggleNode(currentNode, node) {

            if (currentNode.id === node.id) { //currentNode.id === node.id)

                {

                    if (currentNode.key_id === node.key_id)

                    {

                        currentNode.isActive = !currentNode.isActive;

                    }

                }

            }

            else

            {

                currentNode.nodes.forEach((childNode) => _toggleNode(childNode, node));

            }

        }

 

        const nodes = this.state.nodes;

        nodes.forEach((currentNode) => _toggleNode(currentNode, node));

        this.setState((state) => (state.nodes = nodes));

    }

}

 

class TreeNode extends React.Component {

    render() {

        const node = this.props.node;

        const onToggle = this.props.onToggle;

        let activeChildren = null;

        if (node.isActive && node.nodes.length > 0) {

            activeChildren = (

                <ul>

                    {node.nodes.map((node) => (

                        <TreeNode

                            id={node.id}

                            key={node.key_id}

                            node={node}

                            onToggle={onToggle}

                        />

                    ))}

                </ul>

            );

        }

 

        return (

                <li

                id={node.id} linkpagename={node.linkpagename}

                key={node.key_id}

                onClick={(event) => {

                    event.stopPropagation();

                    onToggle(node);                 

                }}

            >

                <Link to={node.linkpagename} style={{ textDecoration: 'none', color: '#000000' }} >

{node.description}</Link>

             {activeChildren}

                </li>

           

        );

    }

}

export default ProductsTreeView;

thanks

1 Answers

React state is ephemeral, it lives in memory. When the page is reloaded the app is reloaded. Anything in state is reset.

This appears to be a case of needing to persist the React state to longer-term storage so when the page is reloaded the state can be reinitialized.

Here's an example:

const initialState = {
  currentNode: {},
  data: "",
};

class Home extends React.Component 
  constructor(props) {
    super(props);
    this.state = initialState;
    this.setCurrentNode = this.setCurrentNode.bind(this);
  }

  componentDidMount() {
    // initialize from storage
    this.setState(JSON.parse(localStorage.getItem("homeState")) ?? initialState);
  }

  componentDidUpdate() {
    // persist updates to storage
    localStorage.setItem("homeState", JSON.stringify(this.state));
  }

  setCurrentNode(node) {
    this.setState({ currentNode: node });
  }

  render() {
    return (
      <div>
        <table className="Container">
          <tbody>
            <tr width="100%">
              <td className="TreeContainer">
                <ProductsTree setCurrentNode={this.setCurrentNode} />
              </td>
              <td className="BodyContainer">
                <Route path="/Add_Category">
                  <AddCategory key_id={this.state.currentNode.key_id} />
                </Route>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    );
  }
}

If other components have state that needs to persist through page reloads they will also need to do this.

AddCategory

const initialState = {
  ID: "",
  Name: "",
}

const getStorageKey = (id) => `addCategory-${id}`;

export class AddCategory extends React.Component {
  constructor(props) {
    super(props);

    this.state = initialState;
  }

  componentDidMount() {
    const { key_id } = this.props;

    // initialize from storage
    this.setState(
      JSON.parse(localStorage.getItem(getStorageKey(key_id))) ?? initialState
    );

    if (typeof key_id !== 'undefined') {
      this.getName(key_id);
    }
  }

  componentDidUpdate(prevProps, prevState) {
    const { ID } = this.state;
    const { key_id } = this.props;

    if (prevProps.key_id !== key_id) {
      console.log(`key_id: ${key_id}`);
      this.getName(key_id);
    }

    if (prevState.ID !== ID) {
      // persist updates to storage
      localStorage.setItem(getStorageKey(ID), JSON.stringify(this.state));
    }
  }

  async getName(ID) {
    const response await fetch(REQUEST_URL);
    const data = await response.json();
    this.setState({
      Name: data,
      ID,
      loading: false
    });
  }

  render() {
    ...
  }
}

TreeView

class TreeView extends React.Component {
  constructor(props) {
    super(props);
    this.state = { nodes: [] };

    this.toggleNode = this.toggleNode.bind(this);
  }

  componentDidMount() {
    // initialize from storage
    const storedState = JSON.parse(localStorage.getItem("treeview"));

    if (storedState) {
      this.setState(storedState);
    } else {
      axios.get(REQUEST_URL, { 'Content-Type': 'application/xml; charset=utf-8' })
        .then((response) => {
          this.setState(
            { nodes: Node.nodesFromXml(response.data) },
           () => {
              // persist updated state to storage
              localStorage.setItem("treeview", JSON.stringify(this.state));
            }
          );
        })
        .catch(function (error) {
          ...
        });
    }
  }

  ...
}
Related