5

Edit. I rewrote the code to be even more minimalist. The below code is a spike test of my issue.

Here is a video of the issue:

https://i.stack.imgur.com/EhItK.jpg

I have two components.

The first component is named TextEditor (and it is a text editor) but its content is irrelevant - the component could be anything. A simple div with text would be just as relevant.

The next component is named Workflows and is used to render a collection from IndexDB (using the Dexie.js library). I named this collection "workflows" and the state variable I store them in is named workflows_list_array

What I am trying to do is the following:

When the page loads, I want to check if any workflows have a specific ID . If they do, I store them in workflows_list_array and render them. I don't need help with this part.

However, if no workflows with the aforementioned criteria exist, I want to keep the component named Workflows hidden and render the component named TextEditor. If workflows do exist, I want the TextEditor hidden and to display Workflows

The problem is that even though I have it working, when workflows do exist (when workflows_list_array is populated) the TextEditor "flickers" briefly before being hidden and then the Workflows component is displayed.

I can tell this is an async issue but I can't tell how to fix it.

I posted code below and I tried to keep it to a minimum.

Test.js

import React, {useState, useEffect} from "react";
import db from "../services"

function Workflows(props){
    return (

          <div>
             <ul>
               {
                props.workflows.map((val,index)=>{

                     return <li key={index}>{val.content}</li>
                })
               }
             </ul>
          </div>
    )
}


function TextEditor(){

    return (

          <div> TextEditor </div>
    )
}


function Test(props){
   let [workflows_list_array, set_state_of_workflows_list_array] = useState([]);

   let [client_id_number, set_client_id_number] = useState(5);
      useEffect(() => { // get all workflows of the selected client per its ID

         db.workflows.toArray((workflows_list)=>{    // iterate through workflows array
               return workflows_list

         }).then((workflows_list)=>{

             workflows_list.forEach((val)=>{

                   if(client_id_number === val.client_id){
                       set_state_of_workflows_list_array((prev)=>{
                          return [...prev, val]
                       });
                   }
             });

         });


    }, []);



    return(
        <div>
          {workflows_list_array.length ? null : <TextEditor/> }
          {workflows_list_array.length ? <Workflows workflows={workflows_list_array}/> : null}

        </div>
    )
}


export default Test

services.js

import Dexie from 'dexie';
import 'dexie-observable';


var workflowMagicUserDB = new Dexie("WorkflowMagicUserDB");

workflowMagicUserDB.version(1).stores({
    user: "",
    workflows: "++id,client_id,content,title",
    clients: "++id,name",
    calendar_events: "++id,start,end,title"
});

export default workflowMagicUserDB
William
  • 4,422
  • 17
  • 55
  • 108
  • If you are trying to render them conditionally and one of them at a time then you can use a single bool state value instead of two..? – Souvik Ghosh Nov 05 '19 at 04:01
  • I did it that way because I left out some things i intend for the future that involves more conditions. I didn't want to bog the post down with uneeded details. I will take another look and see if I can get rid or refactor some stuff. I might have made it more confusing by removing things before posting. – William Nov 05 '19 at 04:03
  • @SouvikGhosh on my end I refactored the JSX render conditions the way you mentioned and I get the same problem so in terms of the root of the problem I'm not sure it much matters. – William Nov 05 '19 at 04:08
  • I think the `workflowsExist` which you are updating in useEffect is asynchronous. Instead of `{ show_editor_bool ? : null }` try something like `{ current_workflows_arr.length ? : null }`. Also please check if the `current_workflows_arr.length` does have the intended value. – Souvik Ghosh Nov 05 '19 at 04:29
  • I get the same problem. The flicker – William Nov 05 '19 at 04:36

2 Answers2

1

Why don't you include a flag which indicates if you have already got data from IndexDB, something like:

function Test(props){
   const [loading, setLoading] = React.useState(true);
   let [workflows_list_array, set_state_of_workflows_list_array] = useState([]);

   let [client_id_number, set_client_id_number] = useState(5);
      useEffect(() => {
         db.workflows.toArray((workflows_list)=>{
         }).then((workflows_list)=>{
         }).finally(() => setLoading(false)); //when process finishes, it will update the state, at that moment it will render TextEditor or Workflows
    }, []);

   if(loading) return <LoadingIndicator/>; // or something else which indicates the app is fetching or processing data

    return(
        <div>
          {workflows_list_array.length ? null : <TextEditor/> }
          {workflows_list_array.length ? <Workflows workflows={workflows_list_array}/> : null}

        </div>
    )
}


export default Test

When the process finishes, finally will be executed and set loading state to false, after that, your app will render TextEditor or Workflows

  • I'll give this a shot. I forgot that the default way to deal with this issue is to hide it with a loader screen. – William Nov 08 '19 at 23:20
  • The flickering of `` is quite bad UX, I researched and tried to find a solution for you but have no luck. BUT, rethink about it, what you want now is your `Component` must know about if the data coming is empty or not, it can not. So I think to solve your problem, you should take another approach in your UI design. Hope this help. – thelonglqd Nov 11 '19 at 04:25
  • 1
    @dqlgnoleht That doesn't help. All I want is for one component to be displayed if a list is empty on load. If it is not empty then another component is displayed. I should not have to rethink my UX to do this. The technology should be able to do this simple thing – William Nov 11 '19 at 23:25
0

I don't know if you already found a solution but I found using useLayoutEffect solved my issue of component flickering on page load.

leark
  • 1