I try to get LocalStorge value for update Recoil atom object value below code.
const LoginState = atom({
key: 'login',
default : null
});
const Home: NextPage = () => {
const [login, setLogin] = useRecoilState(LoginState)
useEffect(()=>{
let localData = localStorage.getItem("token")
if(login === null || undefined){
if(localData !== null || undefined){
setLogin(localData)
}else{
setLogin(null)
}
}
}, [login])
but it has error like this.
Argument of type 'string | null' is not assignable to parameter of type '((currVal: null) => null) | null'.
Type 'string' is not assignable to type '((currVal: null) => null) | null'**.ts(2345)**
<br>
i reckon this problem is came from type. As far as I know type of localStorage object is string or null How can i solve it?
I found solution when i write question.
atom object need to type. First, you define type that uses in atom
type loginState = {
localValue : string | null
}
Second, add type that was defined to atom object like this
const LoginState = atom<loginState>({
key: 'login',
default : {
localValue : null
}
});
Last, you can fix according to your type
useEffect(()=>{
let localData = localStorage.getItem("token")
if(login === null || undefined){
if(localData !== null || undefined){
setLogin({localValue : localData})
}else{
setLogin({localValue : null})
}
}
}, [login])
Related
The following code works fine but I'm getting a Flow error for it:
case UPDATE_USER: {
return {
...state,
users: state.users.map((user) => {
if (user.id === action.id) {
return {...user, [action.propName]: action.payload};
} else {
return user;
}
})
};
}
The exact message is this:
<U>(
callbackfn: (
value: User,
index: number,
array: Array<User>
) => U,
thisArg?: any
) => Array<U>
any
Missing type annotation for `U`. `U` is a type parameter declared in function type [1] and was implicitly instantiated at call of method `map` [2].Flow(InferError)
I suspect the problem might be related to the way I've defined my Action types for the Reducer:
type ToggleModalAction = {type: typeof TOGGLE_MODAL};
type CancelRequestAction = {type: typeof CANCEL_REQUEST, payload: boolean};
type UpdateCompanyAction = {type: typeof UPDATE_COMPANY, payload: number};
type ResetStateAction = {type: typeof RESET_STATE};
type AddUserAction = {type: typeof ADD_USER, isDirty: boolean};
type UpdateUserAction = {type: typeof UPDATE_USER, id: number, propName: string, payload: string | number};
type RemoveUserAction = {type: typeof REMOVE_USER, id: number};
I've tried several things to resolve it but none work. Any ideas?
It seems that you need to provide the return value for map
case UPDATE_USER: {
return {
...state,
users: state.users.map((user): User => {
if (user.id === action.id) {
return {...user, [action.propName]: action.payload};
} else {
return user;
}
})
};
}
Is is possible to get something like this working in flow?
const flag: boolean = false
const obj: ?string = flag ? "hello" : null
if (flag) {
(obj: string) // Cannot cast `obj` to string because null or undefined is incompatible with string.
}
https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVBjOA7AzgFzAA8AuMAIzjhgFMBDbMAXjCjplxsxwLDnIBWZAPwEATgEtsAc2bEwwsACIAFjRjwlYMtgCuG9BKhgAFEQCUYAN6owp-kLDip086gC+QA
I've got a variable obj that's conditionally set if flag is true. Can I do anything so that flow 'remembers' that if flag==true, obj has been set?
You can cast if you want to, but you are required to test against all other types first.
It's also recommended that you create a custom type for this, but I don't believe that is required here.
Here's a code example:
/* #flow */
type ObjectOrString = {}|string|null;
const x: boolean = false
const constObj:ObjectOrString = x ? "hello" : null
var obj:ObjectOrString = x ? "hello" : null
if ( obj && typeof obj === 'object' ) {
let o = (obj:{});
}
if ( obj && typeof obj === 'string' ) {
let o = (obj:string);
}
if ( constObj && typeof constObj === 'object' ) {
let o = (constObj:{});
}
if ( constObj && typeof constObj === 'string' ) {
let o = (constObj:string);
}
https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgDyARgFZ4DGGhATgMoY0CWAdgOZgC8YA3gL4AfAM6NWbASwCuMGAG50FOCxFgAHgC4wxOHBh4Ahiy5go+mELypFyjGGsiSpdY8rV6o9sdVgA-GABEABZ4MnD+YJpSMqgAbvo0YHBkzmSutAzMntzefkEh8OGR0jDoTFBgABSJZGAAZLVg2Phw5UmkXJzcAORtrl1gAJS8qGBgerZwxhVt6vwD8nyoZZXV7fWNuHgtqx3dIpls-UM8I2N4E1Mz+2LzqItL5VX21DXrTVvlz467YD2pVEdhqNxokpl9knMFg8VuC1g13ttYT8utd2ICTsDzqDuBVYepUWxbvdUEA
I'm trying to query an empty firebase list. The problem is that the observable method subscribe never finish and I can't show to user that ddbb list is empty.
The function getUserAppointmentsByDate(...) is calling getUserAppointments(...), where this.database.list('/appointment/users/' + user_uid) is an empty firebase list for the input user (user_uid).
how should I manage an empty query to firebase?
thanks in advance!
getUserAppointmentsByDate(user_uid: string, start: string, end: string) {
if (typeof (user_uid) == "undefined" || typeof (start) == "undefined" || typeof (end) == "undefined") {
console.error("invalid argument for getPatientReport");
return;
}
return this.getUserAppointments(user_uid)
.map(
(appointment) => {
return appointment
.filter((appointment) => {
var appointmentStart = new Date(appointment.start);
var startFilter = new Date(start);
var endFilter = new Date(end);
//Filter old, not cancelled and not deleted
return (appointmentStart.getTime() < endFilter.getTime())
&& (appointmentStart.getTime() > startFilter.getTime())
&& (appointment.status != AppointmentStatus.CANCELLED);
});
})
}
getUserAppointments(user_uid: string): any {
return this.database.list('/appointment/users/' + user_uid) //*THIS IS AN EMPTY LIST
.mergeMap((appointments) => {
return Observable.forkJoin(appointments.map(
(appointment) => this.database.object('/appointment/list/' + appointment.$key)
.take(1)))
})
}
As the this.database.list('/appointment/users/' + user_uid) return a empty array. Observable.forkJoin(appointments.map( complete without emit any value (that is the expected way of forkJoin works). In this case, you have two options, handling in the complete function.
.subscribe(
res => console.log('I got values'),
err => console.log('I got errors'),
// do it whatever you want here
() => console.log('I complete with any values')
)
or handle in an if statement:
import { of } from 'rxjs/observable/of';
...
return this.database.list('/appointment/users/' + user_uid)
.mergeMap((appointments) => {
if (appointments.length === 0) return of([]);
return Observable.forkJoin(appointments.map(
(appointment) => this.database.object('/appointment/list/' + appointment.$key)
.take(1)))
})
For every instantiation of RemoteEntity, I get an error on the type parameter that This type is incompatible with empty, referencing the null value for value in newRemoteEntity:
export type RemoteEntity<T: Identifiable> = {
value: ?T;
error: ?Error;
// ...
}
export function newRemoteEntity(): RemoteEntity<*> {
return {
value: null, // error
error: null, // OK
// ...
}
}
If I instead declare value: ?Object, these errors go away (but then I get other errors related to the loss of my type bound). Am I missing something or is this Flowtype bug/quirk?
I found a workaround by making the fields optional (instead of required but with a maybe-type). However, it makes other code a little more complicated (since I have to check for nulls instead of just propagating them in object literals), so I would prefer having maybe-types work.
export type RemoteEntity<T: Identifiable> = {
value?: T;
error?: Error;
pendingAction: ?string;
// ...
}
export function newRemoteEntity(): RemoteEntity<*> {
return {
pendingAction: null,
// ...
}
}
export function requested<T: Identifiable>(
state: RemoteEntity<T>, action: string, id?: string): RemoteEntity<T> {
// Maybe-type version was more concise:
// return {
// state: id && state.value && state.value.id === id ? state.value : null,
// error: null,
// pendingAction: action,
// // ...
// }
const result: RemoteEntity<T> = {
pendingAction: action,
// ...
}
if (id && state.value && state.value.id === id) {
result.value = state.value
}
return result
}
in the old FB I added a helper function to get/set values as follows:
// val() -> get(), resolve with value at ref
// val(value) -> set(value), resolve with value
// val(vals) -> update(vals), resolve with vals
Firebase.prototype.val = function(vals) {
let self=this;
if (!vals) {
return this.once('value').then(
snapshot => {
if (typeof snapshot.val() === 'undefined' || snapshot.val() === null) throw 'INVALID_VALUE';
return snapshot.val();
},
err => {
throw err;
});
}
let singleVal=(vals.constructor != Object); // is singleVal then vals is a single value
if (singleVal ) return this.set(vals); // set single value
if (!singleVal) return this.update(vals).then(() => vals); // update multiple values
};
}
I could then do for example return ref.child(...).val();
This function does not run in V3.
How can I extend firebase in that way in V3 ?
thx!
Here's the solution - I find this extension very handy
// val() -> get(), resolve with value at ref, fails with error.code
// val(value) -> set(value), resolve with value, fails with error.code
// val(vals) -> update(vals), resolve with vals, fails with error.code
firebase.database.Reference.prototype.val = function(vals) {
let path=this.toString().substring(firebase.database().ref().toString().length-1);
let valsAsString = (typeof vals==='string' ? vals : JSON.stringify(vals));
if (!vals) {
return this.once('value').then(
snapshot => {
if (typeof snapshot.val() === 'undefined' || snapshot.val() === null) {
console.log('val('+path+') failed (null) ! '+error.message+' ('+error.code+')');
throw 'INVALID_VALUE';
}
return snapshot.val(); },
error => {
console.log('val('+path+') failed ! '+error.message+' ('+error.code+')');
throw error.code;
});
}
let singleVal=(vals.constructor != Object); // is singleVal then vals is a single value
if (singleVal ) return this.set(vals).then( // set single value
() => {
return vals;
}, error => {
console.log('val('+path+','+valsAsString+') failed ! '+error.message+' ('+error.code+')');
throw error.code;
}
);
return this.update(vals).then( // update multiple values
() => {
return vals;
}, error => {
console.log('val('+path+','+valsAsString+') failed ! '+error.message+' ('+error.code+')');
throw error.code;
}
);
};
}