I have a simple component
that maintains an internal state. I have another component
that toggles whether or not <
I'm not an expert in React but particularly your case could be solved very cleanly without any mutable objects.
var StatefulView = React.createClass({
getInitialState: function() {
return {
count: 0
}
},
inc: function() {
this.setState({count: this.state.count+1})
},
render: function() {
return !this.props.show ? null : (
<div>
<button onClick={this.inc}>inc</button>
<div>count:{this.state.count}</div>
</div>
)
}
});
var App = React.createClass({
getInitialState: function() {
return {
show: true,
component: StatefulView
}
},
toggle: function() {
this.setState({show: !this.state.show})
},
render: function() {
return (
<div>
<button onClick={this.toggle}>toggle</button>
<this.state.component show={this.state.show}/>
</div>
)
}
});
ReactDOM.render(
<App/>,
document.getElementById('container')
);
You can see it at jsfiddle.
Hmm, you could use localStorage
, or AsyncStorage
if React Native.
React Web
componentWillUnmount() {
localStorage.setItem('someSavedState', JSON.stringify(this.state))
}
Then later that day or 2 seconds later:
componentWillMount() {
const rehydrate = JSON.parse(localStorage.getItem('someSavedState'))
this.setState(rehydrate)
}
React Native
import { AsyncStorage } from 'react-native'
async componentWillMount() {
try {
const result = await AsyncStorage.setItem('someSavedState', JSON.stringify(this.state))
return result
} catch (e) {
return null
}
}
Then later that day or 2 seconds later:
async componentWillMount() {
try {
const data = await AsyncStorage.getItem('someSavedState')
const rehydrate = JSON.parse(data)
return this.setState(rehydrate)
} catch (e) {
return null
}
}
You could also use Redux
and pass the data into the child component when it renders. You might benefit from researching serializing
state and the second parameter of the Redux createStore
function which is for rehydrating an initial state.
Just note that JSON.stringify()
is an expensive operation, so you should not do it on keypress, etc. If you have concern, investigate debouncing.
Since you can't keep the state in the component itself when it unmounts, you have to decide where else it should be saved.
These are your options:
I've you're looking for an easy solution where the data is persisted outside of React, this Hook might come in handy:
const memoryState = {};
function useMemoryState(key, initialState) {
const [state, setState] = useState(() => {
const hasMemoryValue = Object.prototype.hasOwnProperty.call(memoryState, key);
if (hasMemoryValue) {
return memoryState[key]
} else {
return typeof initialState === 'function' ? initialState() : initialState;
}
});
function onChange(nextState) {
memoryState[key] = nextState;
setState(nextState);
}
return [state, onChange];
}
Usage:
const [todos, setTodos] = useMemoryState('todos', ['Buy milk']);
For those who are just reading this in 2019 or beyond, a lot of details have already been given in the other answers, but there are a couple of things that I want to emphasize here:
I'm late to the party but if you're using Redux. You'll get that behaviour almost out of the box with redux-persist. Just add its autoRehydrate
to the store then it will be listening to REHYDRATE
actions that will automatically restore previous state of the component (from web storage).
An easy way of caching state between renders would be to use the fact that modules exported form closure over the file that you are working in.
Using a useEffect
hook you can specify logic that happens on component dismount (i.e. update a variable that is closed over at the module level). This works because imported modules are cached, meaning that the closure created on import never disappears. I'm not sure if this is a GOOD approach or not, but works in cases where a file is only imported once (otherwise the cachedState
would be shared across all instances of the default rendered component)
var cachedState
export default () => {
const [state, setState] = useState(cachedState || {})
useEffect(() => {
return () => cachedState = state
})
return (...)
}