In the getEvaluation function I get the number 1, I want to change the state with this value:
type Props = {
};
type State = {
id_evaluation: string,
};
class Evaluation extends Component < Props, State > {
state = {
id_evaluation: '1',
}
componentDidMount() {
const id_eval = getEvaluation();
this.setState({
id_evaluation: id_eval,
});
I checked now 'console.log(typeof(id_eval)), is string.
Flow generates this error:
Cannot call this.setState with object literal bound to
partialState because null or undefined [1] is incompatible with
string [2].
Try to convert id_eval into a string like this.
type Props = {
};
type State = {
id_evaluation: string,
};
class Evaluation extends Component < Props, State > {
state = {
id_evaluation: '1',
}
componentDidMount() {
const id_eval = getEvaluation();
this.setState({
id_evaluation: String(id_eval) || '',
});
How about this one:
state = {
id_evaluation: 1,
};
const id_eval = getEvaluation();
this.setState({
id_evaluation: id_eval,
});
The return value of the getEvaluation() function has a variety of cases, Number type, Null type or Undefined type, but your State limit accepts the String type, which is the error reported in the compilation phase, you need to convert the return value of getEvaluation(), for example using String(id_eval).
Related
I'm building a generic function that will allow to update nested object properties (ideally based on inferred type - but this is nice to have rather than requirement).
On the top of that please keep in mind - I am not a Typescript expert so I would be glad for pointing out any mistakes.
So here is the example state + types for it:
type DataType = {
knownKeyName1: {
knownNestedKeyName1: string
knownNestedKeyName2: string
knownNestedKeyName3: string
knownNestedKeyName4: string
}
knownKeyName2: {
knownNestedKeyName1: string
knownNestedKeyName2: string
knownNestedKeyName3: string
}
}
type SomeState = {
data: DataType
}
const someState: SomeState = {
data: {
knownKeyName1: {
knownNestedKeyName1: 'value',
knownNestedKeyName2: 'value',
knownNestedKeyName3: 'value',
knownNestedKeyName4: 'value'
},
knownKeyName2: {
knownNestedKeyName1: 'value',
knownNestedKeyName2: 'value',
knownNestedKeyName3: 'value'
}
}
}
And below is the function that I've already tried to build with Typescript's generics:
const setStateValue = <
State extends { data: unknown }, // I want this to infer type from `someState` variable whenever I pass it to this function
Prop extends [keyof State['data']],
VarName extends keyof State['data'][Prop] // Type 'Prop' cannot be used to index type 'State["data"]'.ts(2536)
>(
state: State,
prop: Prop,
varName: VarName,
newValue: string
): void => {
state.data[prop][varName] = newValue
// Example usage should results with:
// state.data[knownKeyName1][knownNestedKeyName3] = 'someNewValue'
}
JavaScript equivalent:
const setStateValue = (state, prop, varName, newValue) => {
state.data[prop][varName] = newValue // Object property reassignment is done on purpose because we want to update the state in reducer with `immer`
}
Any ideas? Additionally, if that's not possible then it would be nice to at least have something like this:
const setStateValue = <
State extends { data: DataType },
Prop extends keyof DataType,
VarName extends keyof DataType[Prop]
>(
state: State,
prop: Prop,
varName: VarName,
newValue: string
): void => {
state.data[prop][varName] = newValue // but then this error pops out:
// Type 'string' is not assignable to type 'DataType[Prop][VarName]'.ts(2322)
}
Thanks in advance!
P.S Adding #redux-toolkit tag to give some usage context, as I want to use this setStateValue function inside generic reducer (so the state can be mutated with immer).
I am attempting to assign FireStore data(), forwarded by props, to a reactive() proxy object, but am receiving the following error:
Object is possibly 'undefined'.
(property) fireStoreData: Record<string, any> | undefined
I wish to use a forEach loop instead of direct assignments i.e. (pageForm.name = props.fireStoreData?.name) to minimize code.
props: {
fireStoreData: Object,
}
...
setup(props){
...
const pageForm: { [key: string]: any } = reactive({
name: '',
address: '',
...
})
onMounted(() => {
Object.keys(pageForm).forEach(key => {
if(key in props.fireStoreData) // <-- error found here
pageForm[key] = props.fireStoreData[key] // <-- and here
})
})
})
...
}
The issie is that the fireStoreData prop is not required yet in your code you assume it is.
try:
props: {
fireStoreData: {
required: true,
type: Object
}
}
If you dont want the prop to be required, its always possible to check for it being defined and add a watcher to look for changes.
Also, dont forget props can change in value.
// on mobile, will format later on pc
// callback functions
const fetchData = (userId:number, cb:any) => {
setTimeout(() => {
const fakeData = {
name: "bob",
id: userId,
pass: 1234
};
cb(fakeData);
}, 500)
}
interface Idata {
name: string,
id: number
}
const afterFetchData = <T extends Idata>(data: T) => {
console.log("your data is", data.pass)
}
How can I put any type of value inside the fakeData and accept it without using any in afterFetchData function ?
now I have this error : Property 'pass' does not exist on type 'T'.ts(2339)
If you know that there will be always some properties in there like id and name, but you are not sure about pass, you can do this :
console.log("your data is", data['pass'])
Otherwise you should leave the fakeData untyped or use any type
I'm using ngrx store.
In my state I have to items
export interface ISchedulesState {
schedulings: ISchedules;
actualTrips: ISchedule[];
}
Here are my interfaces
export interface ISchedules {
[key: string]: ISchedule[];
}
export interface ISchedule {
dest: number;
data: string
}
In reducer I update actualTrips
export const SchedulingReducers = (
state = initialSchedulingState,
action: SchedulesAction
): ISchedulesState => {
switch (action.type) {
case ESchedulesActions.GetSchedulesByDate: {
return {
...state
};
}
case ESchedulesActions.GetSchedulesByDateSuccess: {
return {
...state,
schedulings: action.payload
};
}
case ESchedulesActions.GetSchedulesByTime: {
let time = action.payload;
state.actualTrips = [...(state.schedulings[time] || [])]; // if not data return empty array
return state;
}
default:
return state;
}
};
But actually I get an error
ERROR TypeError: Cannot assign to read only property 'actualTrips' of object '[object Object]'
The basic principle of Redux pattern is immutability of state and its parts, because it let's us to detect changes just by object reference instead of comparing whole objects.
In your reducer, you cannot directly assign a property of state (state.actualTrips =), because change detector (and selectors) would not detect it as changed.
To modify state, you return a copy of the state with new modifications.
const time = action.payload;
return {
...state,
actualTrips: [...(state.schedulings[time] || [])]
}
If you want to change state.actualTrips = myNewValue is not allowed because there is a strict Setting. So one way is may to clonedeep and return the object, like newState = cloneOfState... I didn't test it. So I changed the setting in app.module for Store.
My Example: change the strictStateImmutability to false (full Docu here: https://ngrx.io/guide/store/configuration/runtime-checks )
StoreModule.forRoot(ROOT_REDUCERS_TOKEN, {
metaReducers,
runtimeChecks: {
// strictStateImmutability and strictActionImmutability are enabled by default
strictStateSerializability: true,
strictActionSerializability: true,
strictActionWithinNgZone: true,
strictActionTypeUniqueness: true,
// if you want to change complexe objects and that we have. We need to disable these settings
// change strictStateImmutability, strictActionImmutability
strictStateImmutability: false, // set this to false
strictActionImmutability: true,
},
}),
That error happened me when I changed the input values in the template. I was using Angular11 + NGRX11 so I understood I was changed a value from store, this was my fix:
Before:
this.store.dispatch(new Actions.LoginUser({ user: this.user }));
After:
const clone = {
user: Object.assign({}, this.user)
};
this.store.dispatch(new Actions.LoginUser(clone));
I found the answer at https://stackoverflow.com/a/58279719
Simply copy the state into a new object
const oldState = getState();
let state = JSON.parse(JSON.stringify(oldState)); // here
state.propertyToChange = "newValue";
patchState({
...state
});
I have issue, with passing async data to child component. I trying to write dynamic form generator. Issue starts when I try to call json via Observable and pass it into child component.
service:
generateSearchFields2(): Observable<any> {
return this.http
.get(this.API + 'searchFields')
.map((res:Response) => {
res.json().data as any;
for (var i = 0; i < res.json().data.length; i++) {
var searchField = res.json().data[i];
switch (searchField.component) {
case "TextboxQuestion":
let TXQ: TextboxQuestion = new TextboxQuestion({
key: searchField.key,
label: searchField.label,
value: searchField.value,
required: searchField.required,
order: searchField.order
});
this.searchFieldModels.push(TXQ);
console.log("TXQ: ", TXQ, this.searchFieldModels);
break;
case "DropdownQuestion":
let DDQ: DropdownQuestion = new DropdownQuestion({
key: searchField.key,
label: searchField.label,
required: searchField.required,
options: searchField.options,
order: searchField.order
});
this.searchFieldModels.push(DDQ);
console.log("TXQ: ", DDQ, this.searchFieldModels);
break;
default:
alert("DEFAULT");
break;
}
}
return this.searchFieldModels.sort((a, b) => a.order - b.order);
})
.catch(this.handleError);
}
Component Parent:
generateSearchFields2() {
this.service.generateSearchFields2()
.subscribe(res => this.searchFields = res)
}
Iam passing variable via INPUT directive in parent template to child: [searchFields]="searchFields"
Issue is in child component, where searchField has undefined value. In this child I pass value to another service, to create formContros, but I got undefined there also. Data missing starts here, in child:
#Input() searchFields: SearchBase<any>[] = [];
ngOnInit() {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
Please for hint how I can pass async variable, to not loose data meantime
You can make #Input() searchFields a setter
private _searchFields: SearchBase<any>[] = [];
#Input() set searchFields(value SearchBase<any>[]) {
if(value != null) {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
}
get searchFields() : SearchBase<any>[] {
return this.searchFields;
}
You can also use ngOnChanges() which is called every time an input is updated, but a setter is usually more convenient except perhaps when the executed code depends on multiple inputs being set.
In the ngOnInit event the data which comes from the parent is not bound yet. So your searchFields is undefined yet. You can use it in NgAfterViewInit component lifecycle event.
#Input() searchFields: SearchBase<any>[] = [];
ngAfterViewInit() {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
For other cases you can see Angular2 Component Lifecycle events