I have LOGIN_SUCCESS and CREATE_ACCOUNT_SUCCESS actions.
Both of them should populate user field in auth reducer:
const initialState = {
user: null,
isLoading: false,
error: null
};
Also I have LOGIN_FAILURE and CREATE_ACCOUNT_FAILURE actions. On both actions I want to persist error message in state.
But I don't want to have same error message for both login and create account pages.
How to deal with error field in this case? Should I create 2 error fields in state like this:
const initialState = {
user: null,
isLoading: false,
loginError: null, // <-----
registrationError: null // <-----
};
Or better to have separate reducers for login and for create account? Create account reducer will only persist error message. And auth (login) reducer will handle both LOGIN_SUCCESS and CREATE_ACCOUNT_SUCCESS actions and persist user field.
I don't think you've got a big problem here: logging in and registering are different things, and they need different error states. Have a value for each and keep them separate. You'll probably end up with other things in the reducer that deals with registering anyway (these things never stay simple for long!)
Related
I have seen examples of listening to document changes in streambuilder, but is it possible to use it in providers? I want to listen to changes in the document in userinfo collection.
Here is my code:
in databaseservice.dart
Stream <DocumentSnapshot> get info{
return userinfo.doc(uid).snapshots();
}
In main
return MultiProvider(
providers: [
StreamProvider<DocumentSnapshot>.value(
value: DatabaseService().info
), // other providers
In wrapper where I need to see the change:
final info = Provider.of<DocumentSnapshot>(context).data();
However, I'll first get error:
The method 'data' was called on null.
Receiver: null
Tried calling: data()
And later, the info variable is giving me null instead of a map.
I want to let users input their name and age after their first signup, but not when they sign in. So my idea is that when users sign up, there will be a new document in the collection, "userinfo", which sets their age and name as null at first.
Then the wrapper checks if the name is null. If null, it will turn to the gather information page. If it has a value, it will turn to the home page.
Could anyone tell me where I am doing wrong with my document snapshot thing, or have a better idea to implement this?
React application using Redux. A have a combined reducer, consisting of appStateReducer and contestReducer. Each of these two takes care of some part of the application data.
When action is performed, I want not only the respective state to be changed, but I also want to persistently save the new state, so that if the user reloads application page in the browser, the state would be preserved.
My idea is to add third reducer to take care only of save and load actions (each of the two sub-states separately).
Save and load will use IndexedDB, through localbase package. All of the db actions (add, get, update, delete) appear to be synchronous, i.e. there seems to be no real need to implement asynchronous actions. UPDATE: this is wrong, it is asynchronous, just some basic examples ignore it.
I am not sure how to handle the problem properly.
I will need a database connection object, a singleton, initialized once after page is loaded, which should be shared by all save/load actions regardless of which part of the state is to be stored or loaded. That would lead to a separate reducer working only with the db object. If I do this, the db reducer would have to have access to all the other sub-state, which is normally not the case in Redux.
Or, I could implement save and load action in each reducers separately, not a big deal, actually. But how to make the global db object accessible by the reducers?
It is as React application written in typescript and all components are implemented as classes.
You already have access to all data if you are using middleware, Example:
export const requestPost = (id) => (dispatch,getState) => {
// You can make an bank for post and check If data exist or not
const postState = getState().bank.posts.data;
const found = postState?.find((post) => post.id === id);
if (found) {
dispatch({ type: SUCCESS.POST, data: found });
} else {
dispatch({ type: REQUEST.POST });
API.get(`/post/v2?id=${id}`)
.then((res) => dispatch({ type: SUCCESS.POST, data: res.data[0] }))
.catch((err) => errorHandler(err, FAILURE.POST));
}
};
Just make and reducer for saving data on DB or somewhere and read them at the start.
I switched over to a Redux + Immutable JS project from Ember a few months ago and am overall enjoying the experience.
One problem I still have not found a nice solution for when working with Records is storing meta data for that Record.
For example, let's say I have a User record:
const userRecord = Immutable.Record({
id: null,
name: '',
email: ''
});
For the User, I may also wish to store properties like isLoading or isSaved. The first solution would be to store these in the userRecord. Although this would be the easiest solution by far, this feels wrong to me.
Another solution might be to create a User Map, which contains the User Record, as well as meta data about the User.
Ex.
const userMap = Immutable.Map({
record: Immutable.Record({
id: null,
name: '',
email: ''
}),
isLoading: false,
isSaved: true
});
I think this is more elegant, but I don't like how all the user properties become even more deeply nested, so accessing User properties becomes very verbose.
What I miss most about Ember is being able to access Model properties easily.
Ex. user.get('isSaved') or user.get('name')
Is it possible to recreate something like this with Redux and Immutable? How have you approached this situation before?
I might be misunderstanding the problem, because
What I miss most about Ember is being able to access Model properties easily.
user.get('isSaved') or user.get('name')
This does work for Immutable records.
If you don't want to add too many properties to your record, you could have a single status property and add some getters (assuming your statuses are mutually exclusive):
const STATUS = {
INITIAL: 'INITIAL',
LOADING: 'LOADING',
SAVING: 'SAVING
};
class UserRecord extends Immutable.Record({
id: null,
name: '',
email: '',
status: STATUS.INITIAL}) {
isLoading() {
return this.get('status') === STATUS.LOADING;
}
isSaving() {
return this.get('status') === STATUS.SAVING;
}
}
new UserRecord().isLoading()); // returns false
new UserRecord({status: STATUS.LOADING}).isLoading(); // returns true
new UserRecord().set('status', STATUS.LOADING).isLoading(); // returns true
Sorry for my english. I use the package useraccounts:bootstrap for login, registration and so on. How can I add arbitrary data to Meteor.users collection after registration. For example, I want, that users after registration had a field 'status' with a value of 'false' or the field 'time' with time of registration. Thank you.
If the user needs to supply the data, you will need to customize the UI and add the desired fields.
On the server, you can attach an onCreateUser() callback to set the data when a new user is created.
import _ from 'lodash';
Accounts.onCreateUser((options, user) => {
// add your extra fields here; don't forget to validate the options, if needed
_.extend(user, {
status: false,
createdAt: new Date()
});
return user;
});
the options argument contains the data from the client side.
useraccounts:bootstrap provides you a way to customize your registration panel templates by adding visible, explicit and editable fields into the registration form, as explained in useraccounts/core's GitHub documentation(look for AccountTemplates.addFields method).
However, useraccounts:bootstrap is dependent on accounts-password, so you can use its Accounts.createUser method, simply by passing additional fields in the object passed into Accounts.createUser method. Your createUser method would be like:
Accounts.createUser({
username:'newuser',
password:'pass1234',
profile:{ //no sensitive data here, this can be modified by the user
},
registrationTime: new Date, //date & time of registration
status: false
});
This problem was discussed on Meteor forums:forums.meteor.com.
A more elegant way of solving your problem is calling a server-side function Accounts.onCreateUser every time a user account is created. This function would assign the registrationTime and status to the newly created account. Check this in Meteor's docs: Accounts.onCreateUser docs.meteor.com
Here is how I'm doing it; matches the meteor docs style and doesn't require lodash:
import { Accounts } from 'meteor/accounts-base';
Accounts.onCreateUser((options, user) => {
const userToCreate = Object.assign({
status: false,
createdAt: new Date(),
}, user);
if (options.profile) userToCreate.profile = options.profile;
return userToCreate;
});
I am trying to subscribe to profdle information of a different user than the logged in user, but I am facing issues as mentioned below
I am using angular-material and my code looks like below:
//publish user info upon following user
Meteor.publish("getUserInfo", function (userId) {
return (Meteor.users.find({_id: userId}, {fields: {profile: 1}}));
});
//subscribe
$scope.$meteorSubscribe("getUserInfo", askLikeController.$root.askLike[0].userId).then(function (subscriptionHandle) {
//Second element in the userProfile array will have the profile of required user
askLikeController.$root.usersProfile = $meteor.collection(Meteor.users, false);
});
Issues:
1. In the variable askLikeController.$root.usersProfile, I am getting both the loggedIn user and the desired userinfo having userId, I was expecting userinfo of only desired userId, why is this?
2. The subscription "getUserInfo" is not reactive, and even the subscription is lost after processing few blocks of code and then in the askLikeController.$root.usersProfile I am left with only user profile of logged in user, my guess is that my subscription is being replaced by inbuilt Meteor subscription for user.
How do I solve the issues?
Regards,
Chidan
First, make sure you have removed autopublish:
> meteor remove autopublish
To get reactivity in angular-meteor you need $meteor.autorun and $scope.getReactively. Here's an example:
// we need the requested id in a scope variable
// anytime the scope var changes, $scope.getReactively will
// ... react!
$scope.reqId = askLikeController.$root.askLike[0].userId;
$meteor.autorun($scope, function() {
$scope.$meteorSubscribe('getUserInfo', $scope.getReactively('reqId')));
}).then(function(){
askLikeController.$root.usersProfile = $meteor.collection(Meteor.users, false);
})
Getting only the user you selected: NOTICE- the logged in users is always published. So you need to specify which user you want to look at on the client side, just like you did on the publish method. So, in the subscribe method:
askLikeController.$root.usersProfile = $meteor.collection(function() {
return Meteor.Users.find({_id: $scope.getReactively('reqId')})
}, false);
At this point you might be better off changing it to an object rather than a collection:
askLikeController.$root.usersProfile = $scope.$meteorObject(Meteor.Users, {_id: $scope.getReactively('reqId')});