trying to understand .apply(undefined, arguments)) - reactjs

I've been developing an app in react.
Just a Simple app, it has a feature where I check toggle/toggle state for items inside a list.
At utils.js I have
export const partial = (fn, ...args) => fn.bind(null, ...args)
const _pipe = (f, g) => (...args) => g(f(...args))
export const pipe = (...fns) => fns.reduce(_pipe)
but the there is a problem in App.js, when using the utils:
const getToggledTodo = pipe(findById, toggleCompleted)
the helpers' imports seem fine:
import {pipe, partial} from './lib/utils'
import {addTodo, generateId, findById, toggleCompleted,
updateTodo, removeTodo, filterTodos} from './lib/todoHelpers'
Still , the app complains
Uncaught TypeError: Cannot read property 'find' of undefined
doing console I get:
f2: function () {
return g(f.apply(undefined, arguments));
}
I looked at:
at findById (todoHelpers.js:15)
at utils.js:10
at Object.executeOnChange (LinkedValueUtils.js:132)
and seems to me the undefined is coming from linkedValue file at last line:
executeOnChange: function (inputProps, event) {
if (inputProps.valueLink) {
_assertValueLink(inputProps);
return inputProps.valueLink.requestChange(event.target.value);
} else if (inputProps.checkedLink) {
_assertCheckedLink(inputProps);
return inputProps.checkedLink.requestChange(event.target.checked);
} else if (inputProps.onChange) {
return inputProps.onChange.call(undefined, event);
}
}
};
Not sure how .apply and .call relate to each other here, and seems to me that I'm missing an argument somewhere.
The final objective is to update of state complete/not complete in the db, plus an message in the UI saying that in fact the item has been updated.
Fun fact: if I hard code some similar structured object in App.js and use it in memory to change state, the error does not show... o_O.
It only appears when trying to connect to a 'db', which is still of course a mock. Don't know if its related but I think is worth mentioning.
Using json-server to mock db objects.
So my question is: how to debug this error? Can someone help me understand a bit how apply and call relate to this error.
Any pointers in the right direction would be very helpful and much appreciated.

Related

trying to call api with multiple objects react JS

I'm trying to call a specific object within an api but it returns error
...
calling the first one works but when i call the second objects it returns error
here's the code
Api link : https://opentdb.com/api.php?amount=3&difficulty=easy&type=multiple
const [quiz,setQuiz] = React.useState([""])
React.useEffect(() => {
async function getQuiz() {
const res = await fetch(
"https://opentdb.com/api.php?amount=3&difficulty=easy&type=multiple"
);
const data = await res.json();
setQuiz(data.results);
}
getQuiz();
});
return (
<QuizPage
questionOne={quiz[0].question} //this works //
questionsTwo={quiz[1].question} //this not //
/>
);
I tried to create separate states but it did not work either
i tried to create a seperate state and calling a second object but that didnt work either, i also tried to map over it and still didnt work, i tried many other methods to call api but still no solutions and i get this error in the console ncaught TypeError Cannot read properties of undefined reading 'question, Consider adding an error boundary to your tree to customize error handling behavior, logCapturedError
const [quiz,setQuiz] = React.useState([""])
Your initial sate has only one element, so quiz[1] is undefined, and quiz[1].question will throw an error. You will need to check for the case where you don't have data and either show nothing, or show some loading screen. For example:
const [quiz,setQuiz] = React.useState(null)
// ...
if (!quiz) {
return <div>Loading...</div>
} else {
return (
<QuizPage
questionOne={quiz[0].question}
questionsTwo={quiz[1].question}
/>
);
}

How can I test all this logic inside setState callback parameter

I'm trying to test the following code. I'm using jest and react testing library. This is the firs time I've used setState like this. I solved my initial which was to avoid passing in the dependency of current state but I'm not sure how can I test this. Can someone please advise.
useEffect(() => {
setUsers(currentUsers => {
if(currentUsers === undefined) {
return userDataFromApi;
} else {
//Users already exist in state
const mergedUserData = currentUsers.map(existingUser => {
const matchedUser = userDataFromApi.find(user => user.name === existingUser.name);
if (matchedUser) {
existingUser.stats = user.stats;
}
return existingUser;
});
return mergedUserData;
}
});
}, [setUsers, userDataFromApi]);
This piece of code is implementation detail. React testing library enforces UI testing. You can read this article from Kent Dodds.
In your tests you can do the same thing as the user would do (fill a form, click etc.), and then check what the user should see or not see (maybe his name, his stats etc.).
And if you get data from your backend and you would like to test only the frontend, you can mock the answer of the backend.

Apollo error handling in one point in application

I would like to add error handling in my apollo react app. I'm checking error
export enum ERROR_CODES {
ERROR_USER_NOT_EXIST = "ERROR_USER_NOT_EXIST"
}
export const getErrorMessage = (error: string): string | null => {
switch(error) {
case ERROR_CODES.ERROR_USER_NOT_EXIST:
return 'test error';
default:
return null;
}
}
and I want to show snackBar for errors which I have in switch case.
I understand that i can't do it with apollo-link-error because i want to show an error like react component and I don't want to add error handling for each query request in my components. Maybe exist way to do it in one point in my app and without apollo-link-error.
Use apollo-link-error ;)
Nobody forces you to only console.log() error or break a data flow. You can use it to "inject your hook" only, to be notified on errors.
You can wrap your app in some context provider - create a 'communication channel' - use it to write errors (from error link) and to render (read and clear) them in snackBar.

Jest mock an import from a utility file

I am trying to standardise the way that regularly mocked functions get mocked.
So I have a function which handles a call to a home screen context in useHomeScreenContext.ts.
export const useHomeScreenContext = () => useContext(/* context here */)
Then the component I am testing uses that hook
Component.tsx
export const Component = () => {
const context = useHomeScreenContext();
}
Then my test file for Component looks like
Component.test.tsx
const contextSpy = jest.spyOn(context, 'useHomeScreenContext');
it("works", () => {
contextSpy.mockReturnValue(fakeValue)
expect(fakeValue).toBeTruthy()
})
The issue that I have with this approach is that there will be a large number of files that will need to be tested by mocking this context and the actual mock is a little bit more complex than I have put in here. So what I would like to do is to standardise the mock using a utility function, so I have created a separate file called mockHomeScreenContext.ts which looks a little something like this.
const contextSpy = jest.spyOn(context, 'useHomeScreenContext');
export const mockHomeScreenContext = (context) => {
beforeAll(() => {
contextSpy.mockReturnValue(mergeDeepLeft(context, homeScreenContextDefaults));
});
afterAll(() => {
contextSpy.mockRestore();
});
};
With the idea being that it is used inside of a describe block and it will tidy itself up at the end of the describe block like so
describe('and does not have funds', () => {
mockHomeScreenContext(contextOverrides);
it("works", () => {
// Tests here
})
});
And this seems to work really well, until I have 2 describe blocks and then things start to go a bit haywire and some tests seem to mock the data correctly but then all following tests will fail as the mock function is not returning anything. Getting rid of the afterAll call to clean up the mock helps but it then infects other tests.
I have been round and round in circles for days trying to get this working and I feel like it really shouldnt be this difficult and I am just missing a little bit of an understanding as to how jest mocks work.
I think the problem is in the fact that your contextSpy is global for all tests, because it's created outside of mockHomeScreenContext function. As you can find in documentation, mockRestore would restore the original (non-mocked) implementation. You might try to use mockClear instead, as it's a softer version (I usually just use that).
However, I would propose to not mock your context like this, but rather create a mock provider that would cause context to work properly, without a need to do extensive mocking (which as you probably see can be quie painful).
Here's example of what I have in mind:
Usage in test:
<MockProvider context={contextOverrides}>
<YourComponent/>
</MockProvider>
And implementation:
const MockProvider = ({context, children}) => {
return (
<YourContext.Provider value={mergeDeepLeft(context, homeScreenContextDefaults)}>
{children}
</YourContext.Provider>
)
}
This way you would get rid of all those hard mocks and would use real context (with some mocked value).

React error overlay - show for one error then throw a different one for calling code to handle

I currently have a react app where there are some async functions dealing with redux actions. All these actions are wrapped in this Act function to ensure that any unexpected errors are logged and a more user friendly error is sent to the UI code which then displays it to the user.
export function Act<R>(
callback: (dispatch: Dispatch, getState: () => ReduxState) => Promise<R>
): typeof callback {
return async (dispatch, getState) => {
try {
// if the callback works fine, just return it's value
return await callback(dispatch, getState);
} catch (e) {
if (e instanceof UserError) {
// we are expecting the action to throw these, although should still be logged
console.log(`async callback threw user error: ${e}`);
// propogate to UI code.
throw e;
} else {
// Some exception happened that wasn't expected, log a traceback of the error
console.trace(`Error in Async Action: ${e}`);
// HERE IS WHERE I WANT TO IMPROVE ^
// since the error thrown will likely be shown to the user just show a generic message
throw new UserError('something went wrong');
}
}
};
}
This is working fine although sometimes console.trace isn't enough for me to realize something went wrong that I wasn't expecting, either because the error never makes it's way to the UI or
What I would really like to do is when an unexpected error is thrown in these actions and dev mode is on it would show the error overlay that would be shown if this was a floating promise
I tried using reportRuntimeError from react-error-overlay but I obviously didn't import it correctly since it was logged as undefined:
import { reportRuntimeError } from 'react-error-overlay'; // didn't work and couldn't find type definitions for the module
I tried npm install #types/react-error-overlay which wasn't able to find type definitions for that module and I'm not clear on whether that is the right place to even be trying to do this.
Is there a way to show the error that was originally thrown and then return a different one to be handled by the UI code?
Realized about half way through writing my question that React shows the overlay for Promises that throw an error that are never handled, so I just had to make a promise that happens to throw the error I want to show and not handle it anywhere:
else {
// This gets logged as an Unhandled Promise Rejection
// so React will show the overlay for it as well.
void Promise.reject(e);
throw new UserError(fallbackErrorMessage);
}

Resources