简体   繁体   中英

React Hooks and localStorage dont set correct value

Hello I had an idea to make a hook to increase the font size and save preferences in localStorage basically I have a state that goes from 1 to 4, and then when I click the button add I add +1 to the state until I reach number 4 and on the remove button I remove 1 from the state until 1

But I have doubts on how to save this to my location basically if i don't use my useState just with getInitialValue It works normally.

like this gif, If I add the value manually it works:

在此处输入图片说明

but if I try to use my setFont I have problems (as it is saved in localStorage):

在此处输入图片说明

and i got this on localStorage :

在此处输入图片说明

code:

export default function App() {
  const { fontSize, setSize } = useFontSize();
  console.log(fontSize);
  return (
    <div className="App">
      <button
        onClick={() => {
          setSize(fontSize + 1);
        }}
      >
        add
      </button>
      <button
        onClick={() => {
          setSize(fontSize + 1);
        }}
      >
        remove
      </button>
    </div>
  );
}

hook:

export default function useFontSize(defaultSize = { size: 1 }) {
  const [fontSize, _setSize] = useState(getInitialSize);
  function getInitialSize() {
    const savedSize = localStorage.getItem('_size_acessibility_font');
    const parsedSize = JSON.parse(savedSize);
    if (parsedSize) {
      const { size } = parsedSize;
      if (size >= 1 && size <= 4) {
        return size;
      }
    } else {
      return defaultSize.size;
    }
  }

  useEffect(() => {
    console.log(fontSize, 'on useEffect to set on localStorage');
    localStorage.setItem(
      '_size_acessibility_font',
      JSON.stringify({ size: fontSize }),
    );
  }, [fontSize]);

  return {
    fontSize,
    setSize: ({ setSize, ...size }) => {
      console.log(size, 'on function set size');
      if (size > 4) {
        return _setSize(4);
      }
      if (size < 1) {
        return _setSize(1);
      }
      return _setSize(size);
    },
  };
}

example:

https://codesandbox.io/s/focused-newton-x0mqd

I don't know if this is the best logic for this context, if someone can help me.

This seems a tad overengineered and upsets a few hooks idioms. For example, returning a named object pair for a hook is less typical than an array pair. The set function itself is complex and returns the result of the _setSize calls. Naming could be clearer if fontSize matched setSize by using setFontSize .

({ setSize, ...size }) is problematic since the caller is (correctly) providing an integer.

Here's a minimal, complete version that fixes these issues (local storage is mocked since Stack Snippets is sandboxed):

 <script type="text/babel" defer> const localStorageMock = (() => { const storage = {}; return { getItem: k => storage[k], setItem: (k, v) => {storage[k] = v.toString();} }; })(); const {useState, useEffect} = React; const useFontSize = (defaultSize=1) => { const clamp = (n, lo=1, hi=4) => Math.min(hi, Math.max(n, lo)); const clean = n => isNaN(n) ? defaultSize : clamp(+n); const storageName = "_size_acessibility_font"; const fromStorage = clean(localStorageMock.getItem(storageName)); const [fontSize, setFontSize] = useState(fromStorage); useEffect(() => { localStorageMock.setItem(storageName, fontSize); }, [fontSize]); return [fontSize, size => setFontSize(clean(size))]; }; const App = () => { const [fontSize, setFontSize] = useFontSize(); return ( <div> <div>Font size: {fontSize}</div> <button onClick={() => setFontSize(fontSize + 1)}> + </button> <button onClick={() => setFontSize(fontSize - 1)}> - </button> </div> ); }; ReactDOM.render(<App />, document.body); </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

In useFontSize , you return

return {
  fontSize,
  setSize: ({ setSize, ...size }) => {
    console.log(size, 'on function set size');
    if (size > 4) {
      return _setSize(4);
    }
    if (size < 1) {
      return _setSize(1);
    }
    return _setSize(size);
  },
};

However, in App , you call setSize with just a number setSize(fontSize + 1); when it is expecting an object.

If you change useFontSize to return

return {
  fontSize,
  setSize: (size) => {
    console.log(size, 'on function set size');
    if (size > 4) {
      return _setSize(4);
    }
    if (size < 1) {
      return _setSize(1);
    }
    return _setSize(size);
  },
};

It should work.

Note, you will want to clear your current local storage, or add some error checking.

Also note, although it is just an example, both add and remove use fontSize + 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM