问题
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