admin管理员组

文章数量:1415139

I have created a simple Todos app using React, and have implemented localStorage in order to persist data between page refreshes. The implementation is something like this:

loadStateFromLocalStorage() {
  for (let key in this.state) {
    if (localStorage.hasOwnProperty(key)) {
      let value = localStorage.getItem(key);

      try {
        value = JSON.parse(value);
        this.setState({ [key]: value });
      } catch (e) {
        // handle empty string
        this.setState({ [key]: value });
      }
    }
  }
}
saveStateToLocalStorage() {
  for (let key in this.state) {
    localStorage.setItem(key, JSON.stringify(this.state[key]));
  }
}

If needed, my full App.js is here (and the code is hosted on GitHub here):

import React, { Component } from 'react';
import './App.css';
import Search from './ponents/Search.js';
import Todos from './ponents/Todos.js';
import Filters from './ponents/Filters.js';
import { TodoFilter, InitialState } from './mon';
import GitHubIcon from './ponents/GitHubIcon.js'


class App extends Component {

  state = InitialState;

  // Local Storage
  loadStateFromLocalStorage() {
    for (let key in this.state) {
      if (localStorage.hasOwnProperty(key)) {
        let value = localStorage.getItem(key);

        try {
          value = JSON.parse(value);
          this.setState({ [key]: value });
        } catch (e) {
          // handle empty string
          this.setState({ [key]: value });
        }
      }
    }
  }
  saveStateToLocalStorage() {
    for (let key in this.state) {
        localStorage.setItem(key, JSON.stringify(this.state[key]));
    }
  }

  // Lifecycle methods
  ponentDidMount() {
    // Load state from localStorage
    this.loadStateFromLocalStorage();

    // Resizing page
    this.setState({width: window.innerWidth});
    window.addEventListener("resize", this.updateDimensions);

    // Set localStorage on refresh/reload
    window.addEventListener(
      "beforeunload",
      this.saveStateToLocalStorage.bind(this)
    );
  }
  ponentWillUnmount() {
    // Remove listeners
    window.removeEventListener(
      "beforeunload",
      this.saveStateToLocalStorage.bind(this)
    );
    window.removeEventListener("resize", this.updateDimensions);

    // Save state to localStorage
    this.saveStateToLocalStorage();
  }

  updateDimensions = () => {
    this.setState({ 
      width: window.innerWidth
    });
  }

  // Add a new Todo
  handleSubmit = (event) => {
    event.preventDefault();
    if(this.state.currentTodoText !== "") {
      const newTodo = {
        id: Date.now(),
        text: this.state.currentTodoText,
        checked: false
      };
      const todos = [...this.state.todos, newTodo];
      this.setState({todos});
      this.setState({currentTodoText: ""});
      document.querySelector(".search input").value = "";
    }
  }

  // Update current Todo text
  handleChange = (event) => this.setState({currentTodoText: event.target.value})

  resetData = (event) => {
    event.preventDefault();
    this.setState({todos: InitialState.todos});
    this.setState({currentTodoText: InitialState.currentTodoText});
    this.setState({currentFilter: InitialState.currentFilter});
    document.querySelector(".search input").value = "";
  }

  // Delete a Todo
  handleDelete = (todo) => {
    const todos = this.state.todos.filter((td) => td.id !== todo.id)
    this.setState({todos})
  }

  // Check a Todo
  handleCheck = (todo) => {
    const todos = this.state.todos.map((td) => td.id === todo.id ? {...td, checked: !td.checked} : td)
    this.setState({todos})
  }

  // Change Todo filter
  handleFilter = (filter) => {
    switch (filter) {
      case TodoFilter.filterCompleted: {
        this.setState({currentFilter: TodoFilter.filterCompleted})
        break;
      }
      case TodoFilter.filterUnpleted: {
        this.setState({currentFilter: TodoFilter.filterUnpleted})
        break;
      }
      case TodoFilter.all: {
        this.setState({currentFilter: TodoFilter.all})
        break;
      }
      default: {
        this.setState({currentFilter: TodoFilter.all})
        break;
      }
    }
  }

  render() {
    return (
      <div className="App">
        <Search handleChange={this.handleChange} handleSubmit={this.handleSubmit} resetData={this.resetData}/>
        <Filters handleFilter={this.handleFilter} currentFilter={this.state.currentFilter}/>
        <Todos todos={this.state.todos.filter(
          (todo) => {
            switch (this.state.currentFilter) {
              case TodoFilter.filterCompleted: return todo.checked;
              case TodoFilter.filterUnpleted: return !todo.checked;
              case TodoFilter.all: return true;
              default: return true;
            }
          }
        )} handleDelete={this.handleDelete} handleCheck={this.handleCheck}/>
        <GitHubIcon />
      </div>
    );
  }
}

export default App;

This works on desktop, but I hosted the app on Heroku[1] to see if it works on mobile too and data isn't persisting through page refreshes. Does localStorage work differently on mobile vs desktop? How do I implement localStorage to work both on my phone and my desktop?


[1] Since I tested the accepted answer by redeploying the app to Heroku, the link previously here no longer runs off the code which contained my issue and thus I've removed it - See the mit which fixed my issue here.

I have created a simple Todos app using React, and have implemented localStorage in order to persist data between page refreshes. The implementation is something like this:

loadStateFromLocalStorage() {
  for (let key in this.state) {
    if (localStorage.hasOwnProperty(key)) {
      let value = localStorage.getItem(key);

      try {
        value = JSON.parse(value);
        this.setState({ [key]: value });
      } catch (e) {
        // handle empty string
        this.setState({ [key]: value });
      }
    }
  }
}
saveStateToLocalStorage() {
  for (let key in this.state) {
    localStorage.setItem(key, JSON.stringify(this.state[key]));
  }
}

If needed, my full App.js is here (and the code is hosted on GitHub here):

import React, { Component } from 'react';
import './App.css';
import Search from './ponents/Search.js';
import Todos from './ponents/Todos.js';
import Filters from './ponents/Filters.js';
import { TodoFilter, InitialState } from './mon';
import GitHubIcon from './ponents/GitHubIcon.js'


class App extends Component {

  state = InitialState;

  // Local Storage
  loadStateFromLocalStorage() {
    for (let key in this.state) {
      if (localStorage.hasOwnProperty(key)) {
        let value = localStorage.getItem(key);

        try {
          value = JSON.parse(value);
          this.setState({ [key]: value });
        } catch (e) {
          // handle empty string
          this.setState({ [key]: value });
        }
      }
    }
  }
  saveStateToLocalStorage() {
    for (let key in this.state) {
        localStorage.setItem(key, JSON.stringify(this.state[key]));
    }
  }

  // Lifecycle methods
  ponentDidMount() {
    // Load state from localStorage
    this.loadStateFromLocalStorage();

    // Resizing page
    this.setState({width: window.innerWidth});
    window.addEventListener("resize", this.updateDimensions);

    // Set localStorage on refresh/reload
    window.addEventListener(
      "beforeunload",
      this.saveStateToLocalStorage.bind(this)
    );
  }
  ponentWillUnmount() {
    // Remove listeners
    window.removeEventListener(
      "beforeunload",
      this.saveStateToLocalStorage.bind(this)
    );
    window.removeEventListener("resize", this.updateDimensions);

    // Save state to localStorage
    this.saveStateToLocalStorage();
  }

  updateDimensions = () => {
    this.setState({ 
      width: window.innerWidth
    });
  }

  // Add a new Todo
  handleSubmit = (event) => {
    event.preventDefault();
    if(this.state.currentTodoText !== "") {
      const newTodo = {
        id: Date.now(),
        text: this.state.currentTodoText,
        checked: false
      };
      const todos = [...this.state.todos, newTodo];
      this.setState({todos});
      this.setState({currentTodoText: ""});
      document.querySelector(".search input").value = "";
    }
  }

  // Update current Todo text
  handleChange = (event) => this.setState({currentTodoText: event.target.value})

  resetData = (event) => {
    event.preventDefault();
    this.setState({todos: InitialState.todos});
    this.setState({currentTodoText: InitialState.currentTodoText});
    this.setState({currentFilter: InitialState.currentFilter});
    document.querySelector(".search input").value = "";
  }

  // Delete a Todo
  handleDelete = (todo) => {
    const todos = this.state.todos.filter((td) => td.id !== todo.id)
    this.setState({todos})
  }

  // Check a Todo
  handleCheck = (todo) => {
    const todos = this.state.todos.map((td) => td.id === todo.id ? {...td, checked: !td.checked} : td)
    this.setState({todos})
  }

  // Change Todo filter
  handleFilter = (filter) => {
    switch (filter) {
      case TodoFilter.filterCompleted: {
        this.setState({currentFilter: TodoFilter.filterCompleted})
        break;
      }
      case TodoFilter.filterUnpleted: {
        this.setState({currentFilter: TodoFilter.filterUnpleted})
        break;
      }
      case TodoFilter.all: {
        this.setState({currentFilter: TodoFilter.all})
        break;
      }
      default: {
        this.setState({currentFilter: TodoFilter.all})
        break;
      }
    }
  }

  render() {
    return (
      <div className="App">
        <Search handleChange={this.handleChange} handleSubmit={this.handleSubmit} resetData={this.resetData}/>
        <Filters handleFilter={this.handleFilter} currentFilter={this.state.currentFilter}/>
        <Todos todos={this.state.todos.filter(
          (todo) => {
            switch (this.state.currentFilter) {
              case TodoFilter.filterCompleted: return todo.checked;
              case TodoFilter.filterUnpleted: return !todo.checked;
              case TodoFilter.all: return true;
              default: return true;
            }
          }
        )} handleDelete={this.handleDelete} handleCheck={this.handleCheck}/>
        <GitHubIcon />
      </div>
    );
  }
}

export default App;

This works on desktop, but I hosted the app on Heroku[1] to see if it works on mobile too and data isn't persisting through page refreshes. Does localStorage work differently on mobile vs desktop? How do I implement localStorage to work both on my phone and my desktop?


[1] Since I tested the accepted answer by redeploying the app to Heroku, the link previously here no longer runs off the code which contained my issue and thus I've removed it - See the mit which fixed my issue here.

Share Improve this question edited Apr 18, 2019 at 12:51 James Whiteley asked Apr 18, 2019 at 10:18 James WhiteleyJames Whiteley 3,4741 gold badge21 silver badges49 bronze badges 11
  • What do you exactly mean with mobile? Local storage support is by browser and you can check it here: caniuse./#search=localstorage. Your page on heroku works for me with chrome + safari. – Dario Commented Apr 18, 2019 at 10:22
  • @Dario sorry, I use iOS Safari v12.2. That link suggests localStorage should work on my browser... – James Whiteley Commented Apr 18, 2019 at 10:26
  • And is not working for you? If I open the page (safari 12.0.2), save some todo, refresh the page, I see them again... – Dario Commented Apr 18, 2019 at 10:27
  • No, when I refresh the page none of the data I change is saved. How peculiar... – James Whiteley Commented Apr 18, 2019 at 10:29
  • It's not a storage issue? Older Safari versions wont store anything while in private mode, and you only got a limited amount to store when using localStorage. 5-10Mb depending on browser. – Rickard Elimää Commented Apr 18, 2019 at 10:30
 |  Show 6 more ments

1 Answer 1

Reset to default 4

I found the problem, beforeunload safari mobile does not support this event. Look this docs Events/beforeunload

本文标签: javascriptlocalStorage works on desktop but not mobile (iOS version 122)Stack Overflow