How to handle reference in mobx? - mobx

For example I have Payments and have User. It's Many-to-one relationship. But I don't want to have Payments as nested properties of User.
In redux I would live an ID.
//redux
const state = {
users = [{
id: 232,
payments: [123]
}]
payments: [{
id: 123,
user: 232
}]
}
or something like this. What's the way to works with references in mobx?
I would like to use links so I can:
user.payment['doSomething']

The great thing with mobx is what you can store links in observable. All you need is init them.
const user = new User
const payment = new Payment
user.payment = payment
payment.user = user
Now you can do user.payment.value = 'newVal'
Just make sure property payment inside user and property user inside payment are #observable

Related

Nested data in RTKQuery hook getting join data

I am implementing the "favorites" logic in a project where there are some cards showing some info about songs, and a heart icon shows red if that song is in the user favorites or empty if it is not.
I am using Supabase as a backend (SQL) and I already have a "profiles" table, a "songs" table and a "favorites" table with two foreign keys: profile_id and song_id.
In the project I am using RTKQuery and I have already configured the endpoint to obtain the data for the favorites which is like:
endpoints: (builder) => ({
getFavorites: builder.query({
queryFn: async (id) => {
const {data, error} = await supabase
.from('favorites')
.select(`users_id,songs_id (*)`)
.eq('users_id',id)
return { data, error }
},
providesTags: ["Favorites"]
}),
Then I get the data with:
const { data, isSuccess } = useGetFavoritesQuery( id );
I am calling this hook from the component mad for each card, which already have the props. So I can check if the prop "song.id" is in some of the ids inside the "favorites" object.
But the problem I am facing is because the data that I really need is the nested object inside data, and each one of the nested objects have the id that I would use to check if it is === to song.id of the component I am calling from.
I have tried several things but I don't know if I am doing fine. This is one solution that I have tried. First I iterate over data to obtain the object "favorites":
let favorites;
if (isSuccess){
data.forEach(element => {
favorites = element.songs_id;
})
}
And then the boolean for the heart red or empty:
const favoriteIconColor = useMemo(() => {
if (Object.values(favorites).some(item=> item.id === song.id)){
return true
}
} , [favorites, song]);
But that is not working. I am thinking about creating just an array with all of the ids in the first iteration, but I am not sure if I am overcomplicating the issue.
I have also considered that maybe there is a way to obtain the data as I need with the query itself, but not sure about it. Any advice?

Is there a way to present One2Many Relation in correct way in realm?

I have a very simple schema structure in my mobile app:
export const DepositSchema = {
name: "Deposit",
properties: {
uuid: "string",
payments: "Payment[]",
},
primaryKey: "uuid",
};
export const PaymentSchema = {
name: Instance.Payment,
properties: {
payment_amount: "string",
payment_method: "string",
deposit: {
type: "linkingObjects",
objectType: "Deposit",
property: "payments",
},
},
primaryKey: "uuid",
};
So means that every deposit can have zero, one or few payments (it can be paid partially), but one payment should be assigned only to one deposit
I put Payment objects to Realm database as below:
database.manager.write(() => {
if (payment.deposit_uuid) {
deposit = database.manager.objectForPrimaryKey("Deposit", payment.deposit_uuid);
}
const createdDbPayment = database.manager.create(
Instance.Payment,
{
...payment,
status: STATUS_TO_IGNORE,
},
Realm.UpdateMode.Modified,
);
if (deposit && createdDbPayment && deposit.payments.filtered("uuid == $0", payment.uuid).length == 0) {
deposit.payments.push(createdDbPayment);
}
});
Meanwhile, when i try to log payment object gotten from Realm Database it looks that instead of one deposit assigned to this payment, I got array of deposits. (ofc with only one object but it's very annoying)
The question is: Is there a way to connect this schema to present One2Many (not many2many) relationship?
I would like to achieve:
payment.deposit.uuid instead of payment.deposit[0]?.uuidThanks for help
I would like to achieve: payment.deposit.uuid instead of
payment.deposit[0]?.uuid
Whenever LinkingObjects are used, they allow "automatic" navigation back to the parent object - however, they also create a many-many relationship by their nature.
That's why it's LinkingObjects (plural) not LinkingObject.
If there is only ever one to link back to, then your code will work with the index 0 being required to reference to the first element
payment.deposit[0]?.uuid
The benefit of LinkingObjects is the backlink is created for you (it's more of a calculated property than a managed property). On the other hand it's not exactly what you want.
So you'll need to create the backlink manually - and in some cases it makes more sense but you loose the 'automatic' relationship. Using pseudo-code
export const DepositSchema = {
name: "Deposit",
properties: {
payments: "Payment[]"
export const PaymentSchema = {
name: Payment,
properties: {
parent_deposit: "Deposit"
I would suggest adding a "addPayment" function to add a payment to the Deposit. When a payment is passed to the Deposit property, the parent Deposit will add its reference to the payment and then add it to the Payments List.
That will create a forward relationship from Deposits to multiple Payments and then a single backward relationship from Payment back to its parent Deposit.
Then the graph can be transversed in both directions.

How to filter GraphQL query with relational parameters, React Native

I have a React Native App that gives me a list of properties. Each property has it's own page and a link to a screen that lists all the notes that are associated with that property AND that user. So, a user can only see the notes that they have created for this specific property on this screen.
I'm having trouble writing a GraphQL query that gets all notes associated with the current property AND the current user.
My basic instinct is to write it like this...
const getNotes = gql`
query getNotes {
allPropertyNotes(filter: {
AND: [{
propertyId: ${this.props.navigation.state.params.id}
}, {
userId: ${this.props.screenProps.user.id}
}]
}, orderBy: createdAt_DESC) {
id
title
body
}
}
`;
But this doesn't seem to work because the query is defined outside of the component and doesn't have access to this.props (I'm assuming).
My next though was to do it like this
const getNotes = gql`
query getNotes($userId: ID!, $propertyId: ID!) {
allPropertyNotes(filter: {
AND: [{
propertyId: $propertyId
}, {
userId: $userId
}]
}, orderBy: createdAt_DESC) {
id
title
body
}
}
`;
But I'm not sure how to bind the variables to the ID's that I need them to be. Any pointers on how to do this?
If you need only 'query on mount' then just render <Query/> component using props as variables. Example with variables
The same you can do with graphql() options.variables
Passing query (fired at start by default) and [many] mutations as props are for more complex cases. docs
Docs are not verbose here, you have to gather knowledge from many places/pieces or search for tutorials.

React Native Redux subscribing to store changes

I'm building a shopping cart using React native and Redux. There are some actions such as addToCart and removeFromCart which changes the products array within store. I have two question:
1) How can we update store fields like totalPrice of cart? Should I subscribe to the store changes and update the items accordingly? Should one ever subsribe to the store within React Native context and call utility functions
2) My Add to Cart reducer is below. Am I mutating the values of totalPrice and totalItems? If so, how can I update the totalPrice and totalItems without mutating? This is really confusing.
switch (action.type) {
case AddToCart:
return {
...state,
products: [...state.products, action.product],
totalPrice: action.totalPrice,
totalItems: action.totalItems,
};
When architecting redux store, the store state should not have a duplication of information; it should comply with normalization concept.
Indeed, info like totalPrice ,totalItems can be definitely concluded from the rest of state.
This total can be concluded/calculated inside mapStateToProps , neither in reducer nor in middleware.
If this is clear, you will have two reducers:
The first reducer as a database of products : id+ name+ price+....
The second reducer is about Cart which is a set of shopping items : id + quantity
I may propose the following state shape :
{
products : {
'product_1xxx' : {
id: product_1xxx,
price :150,
name: 'T-Shirt'
} ,
'product_2yyy' : {
id: product_2yyy,
price : 35,
name: 'Glass'
}
},
//---second reducer--//
cartItems: {
[
{
'productId': 'product_2yyy',
quantity: 3
}
]
}
}
The example above means you have 2 products , and the user selects the 2nd product with a quantity of 3 items.
then , the action addToCart will be a shape of {type, productId, quantity}
So when user adds/removes items , the action should be handled only by the 2nd reducers (assuming you are using combineReducers ).
Also the component (responsible of rendering the total) should have the following mapStateToProps :
function mapStateToProps({products, cartItems }) {
return {
totalPrice: cartItems.map(({productId, quantity}) => ({quantity, price: products[productId].price }))
.reduce((total, item) => total + (item.price * item.quantity) , 0);
}
}
As you can note, the price info is not redundant but it was concluded from the first reducer by joining by productId.

MobX - User profile different fields and null defaults issue

I have an AuthStore with these userData defaults:
#observable userData = {
username: null,
firstname: null,
lastname: null,
email: null
}
The object is populated on login with the respective fields and looks like this:
#observable userData = {
username: "jdoe",
firstname: "john",
lastname: "doe",
email: "johndoe#website.com" <— This field is special
}
The email field is special, it should only be fetched/existent if it’s on the authenticated user's profile when they view their own profile. When visiting another user’s profile, all the other fields should be existent except email, and are fetched from an api without email.
Since these are 2 different types of users, an auth user with their email present, and when they visit any other user, there is no email, just all the other respective fields. What’s the best way to handle this multi userData use case?
Should there be 2 stores with the same repeated fields except for email? So the above with the email intact in the AuthStore, and then another UserStore with the same fields without the email, and create 2 different react native ownerProfile.js (for auth user) and profile.js (for other users)? This doesn’t feel right...
What’s the best way to store the logged in userData object to retrieve it on application refresh if it’s available, as it currently empties out the profile's userData fields on app refresh and visiting the profile results in an error since I use AuthStore.userData.username.toUpperCase(); and I get an error because it’s trying to run .toUpperCase() on null.
I came across hydrate/toJson but I am confused by the whole thing, and not even sure when/how to implement them or even if they’re the solution to my problem. I am stuck on my project and can’t move forward because of this issue.
Any idea how to solve this using MobX?
I would create a class User
class User {
#observable username = null;
#observable firstname = null;
#observable lastname = null;
#observable email = null;
}
Then you can make UserStore which contains an observable array of User instances and AuthStore which has an observable field self pointing to the current user profile.
create 2 different react native ownerProfile.js (for auth user) and profile.js (for other users)? This doesn’t feel right...
I usually reuse existing components, but if I start adding too many ifs to a component I consider it a clear signal to extract code into a separate component.
It is hard to say when you should split OwnerProfile and Profile and it depends on what you want to show there. Remember that you can do something like this
function UserProfile({ user, otherProps }) {
return (
<common>
<markup>
<goes here>
{ user.email
? <OwnerProfile user={user} {...otherProps} />
: <Profile user={user} {...otherProps} />
</goes here>
</markup>
</common>
);
}
if you need to wrap OwnerProfile and Profile with the same markup (or you can turn UserProfile into a HOC and apply it to Profile and OwnerProfile on export).
What’s the best way to store the logged in userData object to retrieve it on application refresh if it’s available
You can take a look at mobx-contact-list example. Here it loads contacts from localStorage and here it saves it to localStorage.