6

I'm new to Mobx but so far it's been working great and I've managed to get pretty far. I have a react-native application with mobx, and mobx-persist. I'm using axios to pull posts from a Wordpress site. The functionality that I'm trying to improve is an "add to favorites" feature.

Here's my PostsStore:

export default class PostsStore {

// Define observables and persisting elements
@observable isLoading = true;
@persist('list') @observable posts = [];
@persist('list') @observable favorites = [];

// Get posts from Wordpress REST API
@action getPosts() {
  this.isLoading = true;
  axios({
    url: 'SITE_URL',
    method: 'get'
  })
    .then((response) => {
      this.posts = response.data
      this.isLoading = false
    })
    .catch(error => console.log(error))
}

// Add post to favorites list, ensuring that it does not previously exist
@action addToFavorites(id) {
  if (this.favorites.indexOf(id) === -1) {
    this.favorites.push(id);
  }
}

// Remove post from favorites list, ensuring that it does indeed exist
@action removeFromFavorites(id) {
  if (this.favorites.indexOf(id) !== -1) {
    this.favorites.remove(id);
  }
}

}

In my Favorites component, which is intended to render a button to add or remove from favorites, I thought that using an @computed function would've been preferred to determine if the current post being rendered has an 'id' that has been added to the observable 'favorites' array. However, it seems that an @computed function is not allowed to take arguments (a minimum parameter would be the post's 'id' to evaluate if it is in the favorites observable array. I can accomplish the test using an @action but that doesn't seem to update the rendered screen immediately which is the objective. As the code below demonstrates, I'm forced to perform the test with an 'if' statement in the component render.

render () {
  if (this.props.postsStore.favorites.includes(this.props.item.id)) {
    return (
      <Button
        onPress={() => this.props.postsStore.removeFromFavorites(this.props.item.id)}
        title="★"
      />
    )
  }

Does this affect my application's performance? Is there an @computed way to do what I want? Should I just not worry about this since it's kinda working?

4 Answers4

8

Doing this worked:

@computed get isFavorite() {
   return createTransformer(id => this.favorites.includes(id))
}

Called in my view like so:

this.props.postsStore.isFavorite(this.props.item.id)
5

Just for the sake of completeness: mobx-utils provides a way to use arguments in computed functions by now. You can use computedFn and would declare your function as follows:

isFavorite = computedFn(function isFavorite(id) {
    return this.favorites.includes(id)
})

Take a look at the article in the docs.

Aviv Dolev
  • 140
  • 3
  • 14
Sandrogo
  • 547
  • 7
  • 16
2

I'm not sure @computed is necessary here as it will create a new createTransformer when called everytime after this.favorites changes.

This should produce the same result with only using a single createTransformer

isFavorite = id => createTransformer(id => this.favorites.includes(id))
Ryan King
  • 3,538
  • 12
  • 48
  • 72
0

You can simply wrap it:

@computed
get isFavorite(): (id: string) => boolean {
    return (id: string) => {
        return this.favorites.includes(id);
    };
}

And see the options the official MobX docs suggests: https://mobx.js.org/computeds-with-args.html

PS. I recomend to re-check whether you really need the computed here. The regular method might be good enough in your case.

kli
  • 456
  • 3
  • 12
  • No, you cannot simply wrap it because getters don't take arguments. It says it right in the docs that you have linked. – Danila May 11 '23 at 14:12