Search code examples
reactjslocal-storagedarkmode

How can i save choosen theme in LocalStorage?


Items in localStorage are working just fine, adding, deleting and saving works as it should, but if i choose dark theme and reload page, it will be light again, how can i fix it?

class TodoList extends React.Component { 
  constructor(props) {    
    super(props);     
    this.state = {    
      items: [],
      theme: 'theme' ? 'dark' : 'light'
    };
    this.switchTheme = this.switchTheme.bind(this); 
    this.addItem = this.addItem.bind(this); 
    this.deleteItem = this.deleteItem.bind(this);
  }
  switchTheme() {
    const theme = this.state.theme === 'light' ? 'dark' : 'light';
    this.setState({ theme });
    document.documentElement.setAttribute('data-theme', theme);
  }

  addItem(e) {
   ...
  }

  deleteItem(key) {  
   ...
  }

render() {
      return (
   ...
        <button className='button-switch' onClick = { this.switchTheme }>Switch theme</button>
    );
  }

  componentWillMount() {
    this.setState({
      items: JSON.parse(localStorage.getItem('items')),
      theme: JSON.parse(localStorage.getItem('theme'))
    })
  }
  componentDidUpdate() {
    localStorage.setItem('items', JSON.stringify(this.state.items));
    localStorage.setItem('theme', JSON.stringify(this.state.theme));
  }
}
export default TodoList;

Solution

  • In your sample code, you are only updating the data-theme html attribute inside the switchTheme function:

    document.documentElement.setAttribute('data-theme', theme);
    

    So in your example, you also need to set the attribute before the component mounts for the first time, inside your componentWillMount:

      componentWillMount() {
        this.setState({
          items: JSON.parse(localStorage.getItem('items')),
          theme: JSON.parse(localStorage.getItem('theme'))
        })
    
        document.documentElement.setAttribute('data-theme', this.state.theme);
    
      }
    

    However, componentWillMount is deprecated and you shouldn't use it if possible.

    Here is a codesandbox example that removes the deprecated method and instead sets the theme from local storage inside the constructor.

    See the getInitialTheme function called from the constructor when the component renders:

    constructor(props) {
        super(props);
    
        this.state = {
          items: [],
          theme: this.getInitialTheme()
        };
    
        this.switchTheme = this.switchTheme.bind(this);
      }
    
      // checks if local storage is available, and returns the value from it if so. if not, returns
      // a default of 'light'
      getInitialTheme() {
        try {
          const savedTheme = JSON.parse(localStorage.getItem("theme")) || "light";
          // make sure we update the data-theme property with the localstorage value when the component
          // first renders
          document.documentElement.setAttribute("data-theme", savedTheme);
          return savedTheme;
        } catch {
          // local storage is not available, e.g. the user is using incognito
          return "light";
        }
      }