2

I am using a store in react using Zustand where I create a timer that updates the time every second using a setInterval() function.

I created it in a store in hopes to pass the time to various children components while keeping the time stored and shared from a central location.

I am able to update time in the store and log it successfully but when I pass it to the child components they are not updating the time. How can I pass the updated time and cause a re-render of the time every second it's passed? I do not want to use a useEffect() in the child components.

ZUSTAND STORE

import create from "zustand";

import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
dayjs.extend(utc);
import toObject from "dayjs/plugin/toObject";
dayjs.extend(toObject);
import timezone from "dayjs/plugin/timezone";
dayjs.extend(timezone);


import { IGlobalStyles} from "utils/types";
import { useEffect } from "react";

interface IStyles {
  time?: any;
}

const timer = setInterval(function () {
    
    const time = dayjs().format("hh:mm:ss A")
    console.log("Time in store", time);
    return(time)
}, 1000);


const useGlobalStyles = create<IStyles>((set) => ({
    time: timer
 
})

);

export default useGlobalStyles;

CHILD COMPONENT(This is where I want to display the time)

import React from "react";
import {
  StyleSheet,
  TouchableOpacity,
  Text,
  Button,
  View,
  Dimensions,
} from "react-native";
import {
  NavigationRouteContext,
  useNavigation,
} from "@react-navigation/native";
import * as Shadows from "../../styles/shadows";

import useGlobalStyles from "stores/GlobalStyles";

interface IDateTime {}

export default function DateAndTime({}: IDateTime) {

  //Access zustand store and retreive the current updated time
  const time = useGlobalStyles((store) => store.time);

  return (
    <View style={[styles.container(window.width), styles.shadow]}>
      {/*I want this text component to update time from react Zustand Store*/}
      <Text>{time}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: (containerWidth: any) => ({
    borderWidth: 1,
    position: "absolute",
    top: 30,
    right: 25,
    backgroundColor: "#FFFFFF",
    justifyContent: "center",
    //marginBottom: "5%",
    // width: 422,
    // height: 128,
    width: containerWidth * 0.45,
    height: "8%",
    borderRadius: 10,
    padding: 20,
  }),
  text: {
    alignSelf: "flex-end",
    alignItems: "center",
    fontSize: 10,
    color: "black",
    fontWeight: "700",
    height: 20,
    textAlign: "center",

  },
  shadow: { ...Shadows.componentShadow },
});


Matt Laszcz
  • 373
  • 3
  • 20
  • if you don't wanna use `useeffect` the component will render only one time. so the set-interval will not work even if it is updating in the parent component – Khant Feb 04 '22 at 18:19
  • Is there a way to render the time in the child without useEffect? Using useEffect in the child to listen every second seems redundant since its already doing that in state... – Matt Laszcz Feb 04 '22 at 18:20
  • `timer` is just the id of the timer. It's not some kind of self-updating variable. You'll have to update your store every second in your interval code. – windowsill Feb 04 '22 at 18:25
  • you can try useMemo hook if you wanna make some comparison before updating. using that will make a shallow comparison which will only update or render the child component if the passed state or props change so it will not delay the app. Using `useMemo` for this case is quite expensive and not worth since you are only updating the value – Khant Feb 04 '22 at 18:26
  • is there a specific reason for not using useEffect? Because without useEffect for this particular purpose, it will cost more than what it brings – Deniz Karadağ Feb 04 '22 at 18:31
  • oh ok maybe I just misunderstood then...so I should use useEffect in the child component to listen for a change and should that update the state? Also whats the best way to update the store with the timer Value then and not the id? – Matt Laszcz Feb 04 '22 at 18:35
  • I have made some progress on this from the zustand documentation using const time = useGlobalStyles.subscribe(console.log, (state) => state.time); is successfully listening to state change in child component but I still having trouble updating state in the store every second... – Matt Laszcz Feb 04 '22 at 19:27

1 Answers1

1

I figured out how to accomplish this here is the respective code:

This is the changes to the zustand store: State is now set/updated every second

const useGlobalStyles = create<IStyles>((set) => ({
        width: windowWidth,
        height: windowHeight,
        scale: windowScale,
        time: ""
    })
);

const timer = setInterval(function () {
    
    const currentTime = dayjs().format("hh:mm:ss A")
    console.log("Time in store", currentTime);
    useGlobalStyles.setState({time:currentTime});
    
    
}, 1000);

Here is the child component new code: The time is received from the store, useEffect is used to set the local state of the child component to that new time (causing a re-render) and the state is displayed in a <Text> component to reflect the new time

const [currentTime, setCurrentTime] = useState("");

  //Access zustand store and retreive the current updated time
  const time = timer((store) => store.time);

useEffect(() => {
    setCurrentTime(time);
  }, [time]);

  return (
    <View style={[styles.container(window.width), styles.shadow]}>
      {/*I want this text component to update time from react Zustand Store*/}
      <Text>{currentTime}</Text>
    </View>
  );
}

Matt Laszcz
  • 373
  • 3
  • 20