I came across below codes
const ERROR_MSG = 'ERROR_MSG'
function errorMsg(msg){
return { msg, type:ERROR_MSG }
}
export function register({user,pwd,type}){
return dispatch=>{
axios.post('/user/register', {user,pwd,type})
.then(res=>{
if(res.status!==200){
dispatch(errorMsg(res.data.msg))
}
})
}
}
Its dispatch recieve a function as param, and the param return something. I'm not so comfortable with it, why need to create extra function? It make sense if errorMsg is global or else it will have redundant functions.
is it possible if I use arrow function then dispatch the action object straight away in the callback?
dispatch(()=>{res.data.msg, type:ERROR_MSG})
You can but note that your arrow function is not returning anything:
dispatch(()=>{res.data.msg, type:ERROR_MSG})
Either use an explicit return:
dispatch(()=>{return {res.data.msg, type:ERROR_MSG}})
Or wrap it with an expression:
dispatch(()=> ({res.data.msg, type:ERROR_MSG}))
Or just pass the object directly to dispatch:
dispatch({msg: res.data.msg, type:ERROR_MSG})
Yes is possible to dispatch straight away with arrow function however its not actually required, Instead of calling an extra function in dispatch, you could just dispatch the action with an object
return dispatch=>{
axios.post('/user/register', {user,pwd,type})
.then(res=>{
if(res.status!==200){
dispatch({ msg: res.data.msg, type:ERROR_MSG })
}
})
}
Related
I'm following a NextJS course, however I want to use Typescript - I've read a github discussion forum, but cannot seem to understand why there is an error on the 1st function below, but not the second?
export async function getStaticProps(): GetStaticProps {
return {
props: {
example: ""
}
}
}
The error I get for the above function is the following:
The return type of an async function or method must be the global Promise type. Did you mean to write 'Promise<GetStaticProps<{ [key: string]: any; }, ParsedUrlQuery, PreviewData>>'?
export const getStaticProps: GetStaticProps = async () => {
return {
props: {
example: ""
}
}
};
However I do not get this error for the function above, why is that? What's the difference I thought these 2 functions are the same by definition, one is an arrow function while the other is a regular function?
Can someone please explain how I can use the first function with the appropriate TypeScript types? What's going on?
In the first function, your type specification is of the return type of the function. In the second function, your type specification is of the type of the function itself.
For example, these 2 functions are of the same type, but where you specify the type matters.
const hi: () => string = () => {
return "a"
}
const hi2 = (): string => {
return "a"
}
To answer your second question, all async functions must have return a Promise. As your first function returned GetStaticProps which is not a promise, it gives an error. The way to fix it would be to specify the return type as Promise<GetStaticProps> instead.
EDIT I used the wrong term in the title and question. I did not mean a global variable, but to instead declare timeoutID inside of the showNotification function.
I'm on my first week of testing Redux. I'm wondering if there is a more elegant / less hacky solution to using a glodal variable for the timeoutID? clearTimeout is used to guarantee that the last added notification is always shown for the full desired time, even if it would be added before the previous notification was set to "empty".
actionCreator.js
import { addQuote } from "./addQuote"
import { showNotification } from "./showNotification"
export const actionCreator = (quote, time) => {
return dispatch => {
dispatch(addQuote(quote))
dispatch(showNotification(quote, time))
}
}
showNotification.js
let timeoutID = null
export const showNotification = (newQuote, time) => {
const message = `A new quote by ${newQuote.author} was added.`
return dispatch => {
dispatch({ type: 'SHOW_NOTIFICATION', data: message })
clearTimeout(timeoutID)
timeoutID = setTimeout(() => {
dispatch({ type: 'SHOW_NOTIFICATION', data: '' })
}, time * 1000)
}
}
notificationReducer.js
const initState = {
notification: ''
}
const notificationReducer = (state=initState, action) => {
switch (action.type) {
case 'SHOW_NOTIFICATION':
const message = action.data
return {
...state,
notification: message
}
default:
return {
...state
}
}
}
export default notificationReducer
It's not global, it's a closure, and that's one of the core principles in a module world (which was invented to circumvent having to make use of the global namespace!).
If it were global you could use the variable in any other JS file without ever explicitly importing it.
In actionCreator.js, which does import { showNotification } from "./showNotification", try to console.log(timeoutID) and you'll get undefined.
Closures are really nothing complicated; it just means that any function when declared will "remember" any local variables that were known ("in scope" is the more technically correct term for "known") at the point of the function's declaration, no matter when the function is called, or who calls it. Those variables known to a function via this mechanism are called closures.
There is not only nothing wrong with programming this way; it moreso is state of the art and in contrast to other far more verbose and lengthy solutions like passing parameters around, the most elegant way to solve the problem.
I'm getting the following error:
proxyConsole.js:54 Error: [mobx] Invariant failed: Since strict-mode is enabled, changing observed observable values outside actions is not allowed. Please wrap the code in an `action` if this change is intended. Tried to modify: ObservableObject#1.items
at invariant (mobx.module.js:2326)
at fail (mobx.module.js:2321)
at checkIfStateModificationsAreAllowed (mobx.module.js:2890)
at ObservableValue../node_modules/mobx/lib/mobx.module.js.ObservableValue.prepareNewValue (mobx.module.js:796)
at setPropertyValue (mobx.module.js:1673)
at Object.set [as items] (mobx.module.js:1641)
at Store.js:41
at <anonymous>
But I am wrapping the function in an action so I'm a little confused:
import { observable, useStrict, action } from 'mobx';
import Services from './Services';
// ...
getAllTodos: action(() => {
Services.getAllTodos()
.then((response) => {
state.items = response.data;
}).catch((error) => {
console.error(error);
});
}),
Services.js
// ...
getAllTodos () {
return axios.get(root + '/items/');
}
What am I missing here?
A function that alters the observables needs to be wrapped in action, so use it on the callback as well:
getAllTodos: action(() => {
Services.getAllTodos()
.then(action((response) => {
state.items.replace(response.data);
})).catch((error) => {
console.error(error);
});
})
As stated in the MobX docs here:
The action wrapper / decorator only affects the currently running function, not functions that are scheduled (but not invoked) by the current function! This means that if you have a setTimeout, promise.then or async construction, and in that callback some more state is changed, those callbacks should be wrapped in action as well!
Thus, you'd have to wrap the scheduled promise.then here in an action as well, apart from the parent function. (Note that you'd only be able to use the #action on the class-level function)
There are two ways of doing it:
action(
asyncFunction().then(
action((args) => {
// Your function body here
})
)
)
--or--
Use the #action.bound:
#action
asyncFunction().then(
yourStateModifyingFunction();
)
#action.bound
yourStateModifyingFunction() {
// Your function body here
}
I try to understand arrow functions of typescript by the example of Angular 2 Observable subscribe method. Could somebody explain me:
I have this code which works:
this.readdataservice.getPost().subscribe(
posts => { this.posts = posts; }
);
but should it be the same if I use this? But this doesn't work.
this.readdataservice.getPost().subscribe(
function (posts) {
this.posts = posts;
}
);
Arrow function is anonymous and doesn't bind its own this. Hence, this is this of current context.
Normal function binds this to the caller if we don't bind it explicitly
Then
this.readdataservice.getPost().subscribe(
posts => { this.posts = posts; }
);
Can be
var self = this;
this.readdataservice.getPost().subscribe(
function(posts) { self.posts = posts; }
);
Or
this.readdataservice.getPost().subscribe(
function(posts) { this.posts = posts; }.bind(this)
);
JS by default executes functions in the scope of the caller. If you pass a function around to be called somewhere else, this points to the caller.
In your code you pass the function to the observable via the subscribe(...) method and then the function is called by the observable when an event is to be emitted.
If you use arrow function, then this keeps pointing to the class where it is defined.
An alternative is using .bind(this) to tell JS this should keep pointing at the current class instance.
this.readdataservice.getPost().subscribe(
function (posts) {
this.posts = posts;
}.bind(this)
);
See also https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/Arrow_functions
To learn Redux, I am starting a simplistic web application.
If there is an "authentification" action validating username and password. I then want a "login" or "fail" action to be called. I can not figure out how.
I can call the authentification step, but the nested/following call are not executed.
How is it possible to call a function from within another function? (That's how frustrating my problem boils down ^^)
My code looks like the following:
function authenticateClient(state, client){
if (client.password === 'perfectsec')
return dispatch(Actions.connectClient(client));
else
return dispatch(Actions.refuseClient(client));
}
Calling dispatch in a reducer is incorrect. The simplest would be to call directly the functions connectClient(state, client) or refuseClient(state, client). But then this step does not go through the redux flow.
context code:
/* actions.js */
export function authenticateClient(client) {
return { type: Types.authenticateClient, client };
}
export function connectClient(client, isNew) {
return { type: Types.connectClient, client, isNew };
}
export function refuseClient(client) {
return { type: Types.refuseClient, client };
}
/* main.js */
/* reducer */
const reducer = (state = loadState(), action) => {
console.log(chalk.red(`*${action.type}*`));
switch (action.type) {
case Actions.Types.authenticateClient:
return authenticateClient(state, action);
case Actions.Types.connectClient:
return connectClient(state, action);
case Actions.Types.refuseClient:
return refuseClient(state, action);
default:
return state;
}
};
/* store */
store = createStore(reducer);
/* app */
store.dispatch(Actions.authenticateClient({username, password}));
Reducers should never dispatch actions, or do anything else that has side effects. All a reducer should ever be is a pure function of state and actions (effectively state + action = newState) - the entire Redux library is built around this assumption, and I believe it's specifically designed to throw an error if you try to call dispatch inside a reducer.
The best way to go about conditionally dispatching an action is to create a new 'action creator' - you have a few of these in your application already:
export function authenticateClient(client) {
return { type: Types.authenticateClient, client };
}
export function connectClient(client, isNew) {
return { type: Types.connectClient, client, isNew };
}
export function refuseClient(client) {
return { type: Types.refuseClient, client };
}
Note that standard action creators don't dispatch the action themselves. They're just functions that return an action, which you can dispatch yourself at some point (this is handy if you want to create an action and hold on to it for a little while before actually sending it off). The waters get muddied somewhat once you start writing async action creators, but that's not really in the scope of your problem so I'll skip over it for now (I recommend reading the Async section of the documentation, though! You can do some really cool things once you get your head around it).
What you're trying to express here is a function that says, 'if a condition is true, give me a connectClient action, if not, give me a refuseClient action'. We already know that a function that gives you an action is an action creator, which leads us to the main point of this answer: action creators can have logic. While 90% of them will just take in data and immediately return an object, this isn't the only form they can take. Here's an example of how this functionality could be represented:
export function connectClientIfValid(client) {
if (client.valid === true) {
return connectClient(client);
}
else {
return refuseClient(client);
}
}
Again, nothing actually gets dispatched from this function - it just returns an action, which you can dispatch at your leisure, like so:
dispatch(connectClientIfValid(client));
Your logic runs, and the appropriate action gets dispatched, without having to compromise the pure/synchronous nature of your reducer. All your reducer has to do is read the actions as they come in, and update the state appropriately - it doesn't have to care about the logic that caused the actions to happen.