Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

262
Views
React custom hook's return value changes and yet the component uses the initial return value

I have created a custom hook that dynamically detects and returns the system color theme. The custom hook correctly detects every change and sets the value accordingly. But the component that uses the custom hook always shows the initial value returned by the hook although it re-renders on every theme change.

I would really appreciate if someone can explain why this happens, and can suggest a suitable solution.
Thanks in advance.

useThemeDetector.js

import { useState, useEffect } from 'react';

const useThemeDetector = () => {

  // media query
  const mq = window.matchMedia("(prefers-color-scheme: dark)");

  const [ theme, setTheme ] = useState(mq.matches ? 'dark' : 'light');

  const themeListener = e => {
    setTheme(
      e.matches
        ? 'dark'
        : 'light'
    );
  };

  useEffect(() => {
    mq.addListener(themeListener);
    return () => { mq.removeListener(themeListener); };
  }, [theme]);

  // debug output, shows correct value
  console.log(`theme: ${theme}, from hook`);

  return theme;
};

export default useThemeDetector;

App.js

import Board from './components/Board';
import { useState } from 'react';
import { ThemeContext } from './Context';
import useThemeDetector from './customHooks/useThemeDetector';

const themes = {
  'light': {
    'bgColor': "#fff",
    'fgColor': "#000"
  },
  'dark': {
    'bgColor': "#282c34",
    'fgColor': "#61dafb"
  }
};

function App() {

  const sysTheme = useThemeDetector();

  const [ theme, setTheme ] = useState(sysTheme);
  const [ bgColor, setBgColor ] = useState(themes[theme]['bgColor']);
  const [ fgColor, setFgColor ] = useState(themes[theme]['fgColor']);

  // debug output, shows initial value on every render
  console.log(`theme: ${theme}, from App`);

  const toogleTheme = () => {
    if (theme === 'light') {
      setTheme('dark');
      setBgColor(themes['dark']['bgColor']);
      setFgColor(themes['dark']['fgColor']);
    }
    else {
      setTheme('light');
      setBgColor(themes['light']['bgColor']);
      setFgColor(themes['light']['fgColor']);
    }
  };

  const style = {
    // styles...
  };


  return (
    <ThemeContext.Provider value={{ theme, toogleTheme }}>
      <div
        className="App"
        style={style}
      >
        <Board />
      </div>
    </ThemeContext.Provider>
  );
}

export default App;
about 4 years ago · Santiago Gelvez
1 answers
Answer question

0

The problem is you are effectively using useState twice. There is already a state variable inside the custom hook:

const [ theme, setTheme ] = useState(mq.matches ? 'dark' : 'light');

But then in App.js you are adding another, distinct, state variable:

const [ theme, setTheme ] = useState(sysTheme);

That second variable is different to the one inside the custom hook, and so it just gets initialised to whatever the value of sysTheme was when it was initialised.

Instead, you can do something like this:

  • Get rid of this line const [ theme, setTheme ] = useState(sysTheme);
  • In the custom hook, return the [theme, setTheme] tuple
  • In App.js do const [theme, setTheme] = useThemeDetector(); ...and leave the rest of the code as it is as it is referring to those names.

There may be other issues though with your custom hook. Looks like it will add more and more event listeners each time the theme value changes. You should probably only add an event listener if there is none already there. I'm not 100% sure about this, but I would certainly test it if I were you.

UPDATE: I believe you should make your useEffect hook depend on setTheme and not theme.

about 4 years ago · Santiago Gelvez Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!