Reputation: 65
I'm having a hard time figuring out how to set a component state from localStorage using React and TS. After I check if the localStorage instance exists, it still says incompatible type since localStorage.getItem()
still can return string or null.
If I change my interface and add string | null
I can set the state from localStorage, however I'm no longer able to set the localStorage it self since localStorage.setItem()
seem to only accept string
and not string | null
Edit
I came up with this "solution" to the problem. I'm not sure if it's bad practice but it seems to work. If this can be written in a better way, please let me know.
componentWillMount() {
let from = localStorage.getItem('from');
let to = localStorage.getItem('to');
let fromId = localStorage.getItem('fromId');
let toId = localStorage.getItem('toId');
if (typeof from === 'string' &&
typeof to === 'string' &&
typeof fromId === 'string' &&
typeof toId === 'string') {
this.setState({
inputFrom: from,
inputTo: to,
fromId: fromId,
toId: toId,
});
}
}
The compilation error:
Argument of type '{ inputFrom: string | null; }' is not assignable to parameter of type 'Pick'. Types of property 'inputFrom' are incompatible. Type 'string | null' is not assignable to type 'string'. Type 'null' is not assignable to type 'string'.
My interface is implemented as following:
interface State {
inputFrom: string,
inputTo: string,
fromId: string,
toId: string,
tripdata: {},
loading: boolean,
error: boolean,
}
My component
import * as React from 'react';
import Header from './components/Header';
import SearchForm from './components/search/SearchForm';
import API from './api/APIService';
import TripTable from './components/trips/TripTable';
import Loading from './components/atoms/Loading';
import Error from './components/atoms/Error';
import './App.css';
const api = new API;
interface State {
inputFrom: string,
inputTo: string,
fromId: string,
toId: string,
tripdata: {},
loading: boolean,
error: boolean,
}
class App extends React.Component<any, State> {
constructor() {
super();
this.state = {
inputFrom: '',
inputTo: '',
fromId: '',
toId: '',
tripdata: {},
loading: false,
error: false,
};
}
componentWillMount() {
let from = localStorage.getItem('from');
if (from !== null || from !== undefined) {
this.setState({
inputFrom: from,
});
}
}
handleSubmit = () => {
if (this.state.fromId !== '' && this.state.toId !== '') {
this.setState({error: false});
this.setState({loading: true});
api.GetAccessToken()
.then((token: string) => {
api.GetTripFromSearch(token, this.state.fromId, this.state.toId)
.then((res) => {
this.setState({
tripdata: res,
loading: false,
});
});
localStorage.setItem('from', this.state.inputFrom);
localStorage.setItem('to', this.state.inputTo);
localStorage.setItem('fromId', this.state.fromId);
localStorage.setItem('toId', this.state.toId);
});
} else {
this.setState({error: true});
}
}
render() {
let triptable: JSX.Element | null;
let loading: JSX.Element | null;
let error: JSX.Element | null;
if (this.state.tripdata !== null) {
triptable = <TripTable tripdata={this.state.tripdata} />;
} else {
triptable = null;
}
if (this.state.loading) {
loading = <Loading />;
triptable = null;
} else {
loading = null;
}
if (this.state.error) {
error = <Error />;
} else {
error = null;
}
return (
<div>
<Header />
<SearchForm
resetInputId={this.resetInputId}
handleInputFrom={this.handleInputFrom}
handleInputTo={this.handleInputTo}
handleSubmit={this.handleSubmit}
error={error}
handleSwap={this.handleSwap}
/>
{loading}
{triptable}
</div>
);
}
}
export default App;
Upvotes: 5
Views: 3491
Reputation: 135
In my specific case I was trying to get a boolean out of local storage. Local storage doesn't save booleans. So if you give it 'true' for instance it will save it as a string. That's what the if is for. In practice you can make this look prettier but I used the else if here for clarity.
I placed the checkLocalStorage function outside of the component in this case. I don't see why it wouldn't work inside the component but this post told me to put it outside the component so that's what I did.
ps: If there is nothing in local storage you get a null. That's what I use to set the default value.
const checkLocalStorage = () => {
const ls = localStorage.getItem('item')
if (ls === null || ls === 'true') {
return true
} else if (ls === 'false') {
return false
}
}
export const MyComponent = () => {
const [state, setState] = useState(checkLocalStorage())
// rest of your code here
Upvotes: 1