Angular - “TypeError: Cannot freeze” when dispatching an action

瘦欲@ 提交于 2019-12-23 09:36:35

问题


I'm working on an angular app that uses ngrx store & effects. I get

"TypeError: Cannot freeze"

error when dispatching an action from my component. I wrote it for a file upload feature. I think that I'm mutating the state but cannot figure out where and how to solve it. Here's my detailed code:

Actions:

export enum FileUploadActionTypes {
    UploadFile = '[File Upload] Upload File',
    UploadFileSuccess = '[File Upload] Upload File Success',
    UploadFileFailure = '[File Upload] Upload File Failure'
}

export class UploadFile implements Action {
    readonly type = FileUploadActionTypes.UploadFile;

    constructor(public payload: any) {}
}

export class UploadFileSuccess implements Action {
    readonly type = FileUploadActionTypes.UploadFileSuccess;

    constructor(public payload: any) {}
}

export class UploadFileFailure implements Action {
    readonly type = FileUploadActionTypes.UploadFileFailure;

    constructor(public payload: any) {}
}

export type FileUploadActionsUnion = UploadFile | UploadFileSuccess | UploadFileFailure;

Service:

export class FileUploadService {
    constructor(private httpClient: HttpClient) {

    }
    uploadFile(file: any): Observable<any> {
        return this.httpClient.post<any[]>('/api-url/', file);
    }
}

Effect:

@Effect()
uploadFile$ = this.actions$.pipe(
    ofType<UploadFile>(FileUploadActionTypes.UploadFile),
    mergeMap(action => this.fileUploadService.uploadFile(action.payload).pipe(
        map(result => new UploadFileSuccess(result)),
        catchError(error => of(new UploadFileFailure(error)))
    ))
)

Reducer:

export interface State {
    fileUploadSuccessResponse: any,
    fileUploadFailureResponse: any
}

export const initialState = {
    fileUploadSuccessResponse: null,
    fileUploadFailureResponse: null
}

export const getFileUploadSuccessResponse = state => state.fileUploadSuccessResponse;
export const getFileUploadFailureResponse = state => state.fileUploadFailureResponse;

export function reducer(state: State = initialState, action: FileUploadActionsUnion):State {
    switch(action.type) {
        case FileUploadActionTypes.UploadFile: {
            return {
                ...state
            }
        }
        case FileUploadActionTypes.UploadFileSuccess: {
            return {
                ...state,
                fileUploadSuccessResponse: action.payload
            }
        }
        case FileUploadActionTypes.UploadFileFailure: {
            return {
                ...state,
                fileUploadFailureResponse: action.payload
            }
        }
        default: {
            return state;
        }
    }
}

回答1:


It appears you are using the ngrx-store-freeze npm package. This package ensures that any updates you make to the store are not mutated directly. Part of this process is to apply an Object.freeze() to the payload object of the action you are attempting to dispatch. i.e. this.store.dispatch(new UploadFiles(file)).

Default behavior for the <input type="file"> control is to save it's value as a FileList object. Because this object is a primitive data type, Javascript will treat it as read-only, thus preventing you from writing to it's properties. This also explains why Object.freeze() fails, as it cannot apply a freeze to a read only data type.

I experienced the same issue recently using NGRX and the <input type="file"> form control. The fix was to clone the FileList into a new object before dispatching to the store.

onChange(control: FormControl) {
  const primitiveFileList: FileList = control.value;
  const clonedFiles = { ...primitiveFileList };
  this.store.dispatch(new UploadFiles(clonedFiles));
}

If anyone else knows any potential downfalls to this approach, I'd appreciate your input.



来源:https://stackoverflow.com/questions/52477374/angular-typeerror-cannot-freeze-when-dispatching-an-action

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!