Property does not exist on type 'never' in Ngrx effect - ngrx-effects

I'm using Angular 8 and NGRX 8. I have an action:
export const loadEnvironment = createAction(
LicencingActionTypes.LoadEnvironment,
props<{environment: Environment}>()
);
and corresponding effect:
loadEnvironment = createEffect(() => this.actions$.pipe(
ofType(LicencingActionTypes.LoadEnvironment),
exhaustMap((action) =>
this.authService.
getToken(LicencingEffects.getAuthDetailsForEnvironment(action.environment))
.pipe(
switchMap((token) => [
AuthActions.onLogin({token: token}),
LicencingActions.onLoadEnvironment({environment: action.environment})
]),
catchError(error => of(AuthActions.onLoginError({error: error})))
)
)
));
I've been reading the documentation on NGRX 8 (https://ngrx.io/guide/effects#incorporating-state).
Their example shows that you can just use the action property without casting the type of the action:
...
exhaustMap(action =>
this.authService.login(action.credentials)
...
Webpack won't compile, and I get the following error:
ERROR in src/app/licencing/effects/licencing.effects.ts(20,69): error TS2339: Property 'environment' does not exist on type 'never'.
Screenshot of code with errors highlighted
Where am I going wrong?

Actions must be declared in a type union at the bottom of the actions file:
const all = union({
info,
appError
});
export type CoreActionsUnion = typeof all;
and then in the Effects class constructor injected in with that type as the type parameter of Actions:
constructor(private actions$: Actions<CoreActionsUnion>) { }

Instead of passing the Enum of the action, just pass the function instead. At least it works for me. (Angular 14.x, TS 4.7 and NgrxEffects 13.x, RxJS 7.5.x )
This is what my code looks like where I'm updating a spare part
readonly updatePart$ = createEffect(() =>
this.actions$.pipe(
ofType(updatePart),
concatMap(action =>
this.partService
.updatePart(action.part)
Action itself
export const updatePart = createAction(
PartActions.update,
props<{ part: Required<Part> }>()
);

Try changing your effect as below -
loadEnvironment = createEffect(() => this.actions$.pipe(
ofType(loadEnvironment), //<-- this is your action
exhaustMap(action => //<-- getting rid of extra parenthesis
this.authService.
getToken(LicencingEffects.getAuthDetailsForEnvironment(action.environment))
.pipe(
switchMap((token) => [
AuthActions.onLogin({token: token}),
LicencingActions.onLoadEnvironment({environment: action.environment})
]),
catchError(error => of(AuthActions.onLoginError({error: error})))
)
)
));
Sample reference implementation

Related

Flutter blocTest Missing type arguments for generic function 'blocTest<B extends BlocBase<State>, State>'

When following the bloc test documentation, I created the following blocTest;
blocTest('should do something',
build: () => SignInBloc(signIn: mockSignIn),
act: (bloc) => bloc.add(const OnEmailChanged(email: 'test')));
However I get the intellisense error;
Missing type arguments for generic function 'blocTest<B extends BlocBase<State>, State>'.
And the (bloc) provided in the add is of type Object?.
To fix the issue you have to tell the blocTest what types to expect.
blocTest<SignInBloc, SignInState>('should do something',
build: () => SignInBloc(signIn: mockSignIn),
act: (bloc) => bloc.add(const OnEmailChanged(email: 'test')));
});

Redux Toolkit - do not send request when query param is invalid

I've checked the redux toolkit docs and don't see an example of this typical use case: do not send the request of the query has an invalid param.
For example, a get request to endpoint /categories/{name} requires a name value. If name does not have a value, then the request should not be made.
const baseQuery = fetchBaseQuery({
baseUrl: Constants.PATHWAY_API_URL
});
export const pathwayApi = createApi({
reducerPath: 'pathwayApi',
baseQuery: baseQueryWithReAuth,
endpoints: builder => ({
getSubCategories: builder.query({
// NETWORK REQUEST SHOULD NOT BE MADE IF "name" param is falsy
query: name => `${Constants.PATHWAY_API.CATEGORIES_PATH_NAME}/${name}`,
}),
}),
});
I want to add this type of param validation to all my queries that require a param value or values. What's the recommended approach / pattern for handling this validation at the createApi (or possibly fetchBaseQuery) layer?
Thanks in advance!
You can actually throw an error in your query function.
export const pathwayApi = createApi({
reducerPath: "pathwayApi",
baseQuery: baseQueryWithReAuth,
endpoints: (builder) => ({
getSubCategories: builder.query({
// NETWORK REQUEST SHOULD NOT BE MADE IF "name" param is falsy
query: (name) => {
if (!name) {
throw new Error("Category name is required.");
}
return `${Constants.PATHWAY_API.CATEGORIES_PATH_NAME}/${name}`;
}
})
})
});
When this happens, your hook will have isError: true but no network request will be made. The error property of your hook will be a SerializedError object with properties name, message and stack, which you can use to display the error in your UI.
This is the same type of error object that you get if you have a TypeError somewhere in your code. Note that JavaScript errors will have error.message while API errors (FetchBaseQueryError) will have error.error.
const Category = ({ name }) => {
const { data, error, isError } = useGetSubCategoriesQuery(name);
return (
<div>
<h3>Name: "{name}"</h3>
{isError && (
<div>{error?.error ?? error?.message}</div>
)}
</div>
);
};
CodeSandbox Link

i18n addResourceBundle mocks shows error i18n not defined

I am using react testing library to test my code and I have used i18n.addResourceBundle to add some translations on the fly. I am trying to test its and have
jest.mock('i18n', () => ({
__esModule: true,
default: { addResourceBundle: jest.fn() }
}))
BUt when I try to do snapshot, it keeps saying i18n.addResourceBundle is not defined
You are mocking i18next improperly.
The usage of i18next looks like:
import i18next from 'i18next';
export const i18n = i18next.init({
...
// config
...
});
// somewhere else in the code
i18n.addResourceBundle();
//-^ this is an instance of i18next
That means that you need to return an object with init function which returns an instance with addResourceBundle.
jest.mock('i18n', () => ({
__esModule: true,
default: {
init(config) {
return {
// this is the instance
addResourceBundle: jest.fn(),
};
},
},
}));

received error message when using component

I am creating a website using ReasonReact, but I encounter this error message when using a normal component. Does anyone know what is happening?
module Component1 = {
let component = ReasonReact.statelessComponent("Component1");
let make = () => {...component, render: self => <div />};
};
module Component2 = {
let component = ReasonReact.statelessComponent("Component1");
let make = () => {
...component,
render: self => <div> <Component1 /></div>, /*error on compenent1*/
};
Here is the error message:
(
React.component('props),
'props
) => React.element
<root>/node_modules/reason-react/src/React.re
Error: This expression has type
unit =>
ReasonReact.componentSpec(ReasonReact.stateless,
ReasonReact.stateless,
ReasonReact.noRetainedProps,
ReasonReact.noRetainedProps,
ReasonReact.actionless)
but an expression was expected of type
React.component(unit) = unit => React.element
Type
ReasonReact.componentSpec(ReasonReact.stateless,
ReasonReact.stateless,
ReasonReact.noRetainedProps,
ReasonReact.noRetainedProps,
ReasonReact.actionless)
is not compatible with type React.element
The problem seems to be that you're using a project configured to use JSX version 3 with components designed for JSX version 2.
JSX version 3 was introduced in ReasonReact 0.7.0, along with a new method for defining react components that supports hooks, but still supports the method you're using as long as you configure your project to use JSX version 2. If this is a new project, which it seems to be, I would recommend using the new component style, where your code would simply look like this:
module Component1 = {
[#react.component]
let make = () =>
<div />;
};
module Component2 = {
[#react.component]
let make = () =>
<div> <Component1 /> </div>;
};
Alternatively, you can continue using the old style of components and JSX version 2 by specifying the following in bsconfig.json:
{
...
"reason": {"react-jsx": 2}
}
See the blog post on ReasonReact 0.7.0 for more details.

How to pass context down to the Enzyme mount method to test component which includes Material UI component?

I am trying to use mount from Enzyme to test my component in which a several Material UI component are nested. I get this error when running the test:
TypeError: Cannot read property 'prepareStyles' of undefined
After some digging, I did found that a theme needs to be passed down in a context. I am doing that in the test but still get this error.
My test:
import expect from 'expect';
import React, {PropTypes} from 'react';
import {mount} from 'enzyme';
import SearchBar from './SearchBar';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
function setup() {
const muiTheme = getMuiTheme();
const props = {
closeSearchBar: () => {},
fetchSearchData: () => {},
data: [],
searching: false
};
return mount(<SearchBar {...props} />, {context: {muiTheme}});
}
describe('SearchBar Component', ()=> {
it('Renders search toolbar properly', () => {
const wrapper = setup();
expect(wrapper.find('.toolbar').length).toBe(1);
expect(wrapper.find('button').length).toBe(1);
});
});
My searchbar component is a stateless component, so I am not pulling in any context. But even when I am, I still get the same error.
What am I doing wrong?
Try adding childContextTypes in the mount options:
return mount(
<SearchBar {...props} />, {
context: {muiTheme},
childContextTypes: {muiTheme: React.PropTypes.object}
}
);
By doing it you set the Enzyme wrapper to make the muiTheme available to it's children through the context.
this is my handy method to test Material UI with shallow and mount
...
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import getMuiTheme from 'material-ui/styles/getMuiTheme';
const muiTheme = getMuiTheme();
const shallowWithContext = (node) => shallow(node, {context: {muiTheme}, childContextTypes: {muiTheme: PropTypes.object}});
const mountWithContext = (node) => mount(
node, {context: {muiTheme}, childContextTypes: {muiTheme: PropTypes.object}}
);
// now you can do
const wrapper = shallowWithContext(<Login auth={auth} onChange={() => 'test'} />);