From cb6b7381dc743c394b30d3af30df83a93cb22ae5 Mon Sep 17 00:00:00 2001
From: justinwoo <moomoowoo@gmail.com>
Date: Sat, 25 Jan 2020 14:16:15 +0200
Subject: [PATCH] fix localstorage hook not being able to synchronously get
 state

---
 src/Gargantext/Components/Forest.purs      | 15 +++++++++++----
 src/Gargantext/Components/Forest/Tree.purs |  5 ++---
 src/Gargantext/Utils/Reactix.purs          | 20 ++++++--------------
 3 files changed, 19 insertions(+), 21 deletions(-)

diff --git a/src/Gargantext/Components/Forest.purs b/src/Gargantext/Components/Forest.purs
index bb2ab810..f758aa05 100644
--- a/src/Gargantext/Components/Forest.purs
+++ b/src/Gargantext/Components/Forest.purs
@@ -1,14 +1,19 @@
 module Gargantext.Components.Forest where
 
+import Gargantext.Prelude
+
 import Data.Array as A
 import Data.Maybe (Maybe(..))
+import Data.Set (Set)
+import Data.Set as Set
+import Data.Tuple (fst)
 import Data.Tuple.Nested ((/\))
 import Gargantext.Components.Forest.Tree (treeView)
+import Gargantext.Components.Login.Types (TreeId)
 import Gargantext.Ends (Frontends)
 import Gargantext.Routes (AppRoute)
 import Gargantext.Sessions (Session(..), Sessions, unSessions)
 import Gargantext.Utils.Reactix as R2
-import Prelude (const, pure, ($), (<$>))
 import Reactix as R
 import Reactix.DOM.HTML as H
 
@@ -24,13 +29,15 @@ forest props = R.createElement forestCpt props []
 
 forestCpt :: R.Component Props
 forestCpt = R.hooksComponent "G.C.Forest.forest" cpt where
-  cpt {frontends, route, sessions, showLogin } _ = R2.useCache (frontends /\ route /\ sessions) (cpt' showLogin)
-  cpt' showLogin (frontends /\ route /\ sessions) =
+  cpt {frontends, route, sessions, showLogin } _ = do
+    openNodes <- R2.useLocalStorageState R2.openNodesKey (Set.empty :: Set TreeId)
+    R2.useCache (frontends /\ route /\ sessions /\ fst openNodes) (cpt' openNodes showLogin)
+  cpt' openNodes showLogin (frontends /\ route /\ sessions /\ openNodesState) = do
     pure $ R.fragment $ A.cons (plus showLogin) trees
     where
       trees = tree <$> unSessions sessions
       tree s@(Session {treeId}) =
-        treeView { root: treeId, frontends, mCurrentRoute: Just route, session: s }
+        treeView { root: treeId, frontends, mCurrentRoute: Just route, session: s, openNodes }
 
 plus :: R2.Setter Boolean -> R.Element
 plus showLogin =
diff --git a/src/Gargantext/Components/Forest/Tree.purs b/src/Gargantext/Components/Forest/Tree.purs
index d6f0fc57..309e085f 100644
--- a/src/Gargantext/Components/Forest/Tree.purs
+++ b/src/Gargantext/Components/Forest/Tree.purs
@@ -20,7 +20,6 @@ import Gargantext.Ends (Frontends)
 import Gargantext.Routes (AppRoute)
 import Gargantext.Sessions (Session)
 import Gargantext.Types (AsyncTask(..))
-import Gargantext.Utils.Reactix as R2
 import Reactix as R
 import Reactix.DOM.HTML as H
 
@@ -29,6 +28,7 @@ type Props = ( root          :: ID
              , mCurrentRoute :: Maybe AppRoute
              , session       :: Session
              , frontends     :: Frontends
+             , openNodes     :: R.State (Set TreeId)
              )
 
 treeView :: Record Props -> R.Element
@@ -37,10 +37,9 @@ treeView props = R.createElement treeViewCpt props []
 treeViewCpt :: R.Component Props
 treeViewCpt = R.hooksComponent "G.C.Tree.treeView" cpt
   where
-    cpt { root, mCurrentRoute, session, frontends } _children = do
+    cpt { root, mCurrentRoute, session, frontends, openNodes } _children = do
       -- NOTE: this is a hack to reload the tree view on demand
       reload <- R.useState' (0 :: Reload)
-      openNodes <- R2.useLocalStorageState R2.openNodesKey (Set.empty :: Set TreeId)
       pure $ treeLoadView
         { root, mCurrentRoute, session, frontends, openNodes, reload }
 
diff --git a/src/Gargantext/Utils/Reactix.purs b/src/Gargantext/Utils/Reactix.purs
index 260a1291..fd5ab9cf 100644
--- a/src/Gargantext/Utils/Reactix.purs
+++ b/src/Gargantext/Utils/Reactix.purs
@@ -12,9 +12,8 @@ import Data.Argonaut as Argonaut
 import Data.Argonaut as Json
 import Data.Argonaut.Core (Json)
 import Data.Either (hush)
-import Data.Foldable (for_)
 import Data.Function.Uncurried (Fn2, runFn2)
-import Data.Maybe (Maybe(..), fromJust)
+import Data.Maybe (Maybe(..), fromJust, fromMaybe)
 import Data.Nullable (Nullable, null, toMaybe)
 import Data.Tuple (Tuple(..))
 import Data.Tuple.Nested ((/\))
@@ -23,6 +22,7 @@ import Effect.Aff (Aff, launchAff, launchAff_, killFiber)
 import Effect.Class (liftEffect)
 import Effect.Exception (error)
 import Effect.Uncurried (EffectFn1, mkEffectFn1, mkEffectFn2, runEffectFn1)
+import Effect.Unsafe (unsafePerformEffect)
 import FFI.Simple ((..), (...), defineProperty, delay, args2, args3)
 import Partial.Unsafe (unsafePartial)
 import React (class ReactPropFields, Children, ReactClass, ReactElement)
@@ -267,25 +267,17 @@ type LocalStorageKey = String
 
 useLocalStorageState :: forall s. Argonaut.DecodeJson s => Argonaut.EncodeJson s => LocalStorageKey -> s -> R.Hooks (R.State s)
 useLocalStorageState key s = do
-  ref <- R.useRef s
-
-  R.useEffectOnce' do
+  -- we need to synchronously get the initial state from local storage
+  Tuple state setState' <- R.useState \_ -> unsafePerformEffect do
     item :: Maybe String <- getItem key =<< getls
     let json = hush <<< Argonaut.jsonParser =<< item
     let parsed = hush <<< Argonaut.decodeJson =<< json
-    for_ parsed \(x :: s) -> R.setRef ref x
-
-  let init = R.readRef ref
-
-  Tuple state setState' <- R.useState' init
+    pure $ fromMaybe s parsed
 
   let
     setState update = do
-      let new = update (R.readRef ref)
-
+      let new = update state
       setState' (\_ -> new)
-      R.setRef ref new
-
       let json = Json.stringify $ Argonaut.encodeJson new
       storage <- getls
       setItem key json storage
-- 
2.21.0