React pass cache and get/set function in context

痞子三分冷 提交于 2019-12-24 21:42:35

问题


I cant seem to get the below code to work. Trying to have some simple "cache" in react which I want to pass down from the App component using context. State is present in App as follows:

  const [cacheData, setCacheData] = useState({});
  const getCache = (key) => {
    console.log('Getting value from cache with key ' + key, cacheData);
    return cacheData[key];
  }
  const setCache = (key, data) => {
    try{
      console.log(cacheData);

      console.log('Setting value to cache with key ' + key, data);
      let dataCopy = JSON.parse(JSON.stringify(cacheData));
      dataCopy[key] = data;
      console.log(dataCopy, cacheData);
      setCacheData(dataCopy); 
      console.log('jaja');
    }catch(err){
      console.log(err);
    }
  }

Then it is passed down to context like this:

<CacheContext.Provider value={{data: cacheData, get: getCache, set: setCache}}>

In a child component I use cache.get and cache.set, all have correct console.logs, but the cache is always undefined. Cachedata is always {}. My guess is that the setCache function isnt doing anything.

Thanks in advance guys. Also, if you think I am reinventing the wheel please point me to some help :) couldnt find any package which did this for me.

Snippet: (copied from answer, this one works. Will add the faulty code)

const {useState, useContext, createContext} = React

const fn = () => undefined
const CacheContext = createContext({data: {}, get: fn, set: fn})

const App = () => {
  const [cacheData, setCacheData] = useState({});
  const getCache = (key) => {
    console.log('Getting value from cache with key ' + key, cacheData);
    return cacheData[key];
  }
  const setCache = (key, data) => {
    try{
      console.log(cacheData);

      console.log('Setting value to cache with key ' + key, data);
      let dataCopy = JSON.parse(JSON.stringify(cacheData));
      dataCopy[key] = data;
      console.log(dataCopy, cacheData);
      setCacheData(dataCopy); 
      console.log('jaja');
    }catch(err){
      console.log(err);
    }
  }
  
  return (
    <CacheContext.Provider value={{data: cacheData, get: getCache, set: setCache}}>
      <Main />
    </CacheContext.Provider>
  )
}

const useCache = () => useContext(CacheContext)

const Main = () => {
  const cache = useCache()
  const [key, setKey] = useState('key')
  const [value, setValue] = useState('value')
  
  return (
    <div>
      <input value={key} onChange={(e) => setKey(e.target.value)} /> :
      <input value={value} onChange={(e) => setValue(e.target.value)} />
      <button onClick={() => cache.set(key, value)}>Set</button>
      <div>Existing keys: [{Object.keys(cache.data).join(', ')}]</div>
      <div>Current value of '{key}': {cache.get(key) || 'undefined'}</div>
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>

回答1:


Why aren't you using LocalStorage or SessionStorage(tab specific)? Simply push and take from localStorage and you'll be fine man. Either that or use redux-store along with redux-persist to handle refreshes.

redux: https://www.npmjs.com/package/react-redux

redux-persist: plugin that stores redux-store data to localStorage: https://www.npmjs.com/package/redux-persist




回答2:


The code in your question works fine when used as follows. Please create a Minimal, Reproducible Example of your problem by including following code snippet in your question and editing it until you reproduce the problem:

const {useState, useContext, createContext} = React

const fn = () => undefined
const CacheContext = createContext({data: {}, get: fn, set: fn})

const App = () => {
  const [cacheData, setCacheData] = useState({});
  const getCache = (key) => {
    console.log('Getting value from cache with key ' + key, cacheData);
    return cacheData[key];
  }
  const setCache = (key, data) => {
    try{
      console.log(cacheData);

      console.log('Setting value to cache with key ' + key, data);
      let dataCopy = JSON.parse(JSON.stringify(cacheData));
      dataCopy[key] = data;
      console.log(dataCopy, cacheData);
      setCacheData(dataCopy); 
      console.log('jaja');
    }catch(err){
      console.log(err);
    }
  }
  
  return (
    <CacheContext.Provider value={{data: cacheData, get: getCache, set: setCache}}>
      <Main />
    </CacheContext.Provider>
  )
}

const Main = () => {
  const {data, get, set} = useContext(CacheContext)
  const [key, setKey] = useState('key')
  const [value, setValue] = useState('value')
  
  return (
    <div>
      <input value={key} onChange={(e) => setKey(e.target.value)} />
      :
      <input value={value} onChange={(e) => setValue(e.target.value)} />
      <button onClick={() => set(key, value)}>Set</button>
      <div>Existing keys: [{Object.keys(data).join(', ')}]</div>
      <div>Current value of '{key}': {get(key) || 'undefined'}</div>
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>

That being said, functional updates can be used when the new state is computed using the previous state. And if you discover you need a mutable reference for imperative code, async operations, working with closures and/or code optimized for performance, useRef can be used for accessing future values of data:

const {useState, useContext, useRef, createContext, memo} = React

const fn = () => undefined
const CacheContext = createContext({data: {}, get: fn, set: fn})

const App = memo(() => {
  const [cacheData, setCacheData] = useState({});
  const dataRef = useRef();
  dataRef.current = cacheData;
  const getCache = (key) => {
    console.log('Getting value from cache with key ' + key, cacheData);
    return cacheData[key];
  }
  const setCache = (key, data) => {
    try{
      console.log(cacheData);

      console.log('Setting value to cache with key ' + key, data);
      setCacheData(current => {
        const dataCopy = JSON.parse(JSON.stringify(current));
        dataCopy[key] = data;
        return dataCopy;
      })
      setCacheData(dataCopy); 
      console.log('jaja');
    }catch(err){
      console.log(err);
    }
  }
  
  return (
    <CacheContext.Provider value={{dataRef, get: getCache, set: setCache}}>
      <Main />
    </CacheContext.Provider>
  )
})

const Main = memo(() => {
  const {dataRef, get, set} = useContext(CacheContext)
  const [key, setKey] = useState('key')
  const [value, setValue] = useState('value')
  
  const setAsync = () => {
    set('in progress', key)
    setTimeout(() => {
      set(key, value)
      // notice how mutating a reference will NOT trigger a re-render, unlike using `set`
      // set('in progress', undefined)
      delete dataRef.current['in progress']
    }, 3000)
  }
  
  return (
    <div>
      <input value={key} onChange={(e) => setKey(e.target.value)} />
      :
      <input value={value} onChange={(e) => setValue(e.target.value)} />
      <button onClick={setAsync}>Set</button>
      <div>Existing keys: [{Object.keys(dataRef.current).join(', ')}]</div>
      <div>Current value of '{key}': {get(key) || 'undefined'}</div>
      <div>Current value of 'in progress': {get('in progress') || 'undefined'}</div>
    </div>
  )
})

ReactDOM.render(<App />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>


来源:https://stackoverflow.com/questions/58569798/react-pass-cache-and-get-set-function-in-context

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!