Nozbe / WatermelonDB

🍉 Reactive & asynchronous database for powerful React and React Native apps ⚡️
https://watermelondb.dev
MIT License
10.49k stars 589 forks source link

withObservables() does not react to changes in table #1471

Open Stophface opened 1 year ago

Stophface commented 1 year ago

I am connecting a functional comopnent like this to the database.

const Foo = ({data}) => {
    console.log(data);
    return <SomeCoolUiWithData />
}

export default compose(
  withDatabase,
  withObservables(['data'], ({database}) => {
    return {
      data: database.get('data_table').query().observe(),
    };
  }),
)(Foo);

However, when I update the table data_table, console.log(data) is not logging anything. console.log(data) only logs on the very first render, but not when the table data_table is updated. Why is that? How would I subscribe to changes in the table data_table?

This is the model of data_table

export class SomeClass extends Model {
  static table ='data_table';

  @field('day')
  date;

  @field('count')
  count;

  @field('year)
  year;
}
alicja-mruk commented 1 year ago

Why do you pass data to the observable? Do you want to show a list or an item in this Foo component?

Does wrapping your component into something like that also does not update your data?

const enhanceWithData = withObservables([ ], ( ) => ({
  data_table: database.collections.get('data_table').query().observe(),
}));

export default enhanceWithData(Foo)
Stophface commented 1 year ago

@alicja99 Thamks for your reply. I am not sure if I understand you correctly. Where would be the difference if I would want to display and item or a list? Could you show me how that reflects in the code?

alicja-mruk commented 1 year ago

@Stophface I highly recommend looking at the example of the post project and documentation, everything is setup there. If you want to get the plain list, you don't pass anything into the observers table.

Stophface commented 1 year ago

@alicja99 I am struggeling with the documentationn and the example project. Some things are not working like this for me. When I try what you suggested,

const enhanceWithData = withObservables([ ], ( ) => ({
  data_table: database.collections.get('data_table').query().observe(),
}));

it tells me database is undefined. I need to wrap it in compose

export default compose(
  withDatabase,
  withObservables(['data'], ({database}) => {
    return {
      data: database.get('data_table').query().observe(),
    };
  }),
)(Foo);

to make the database available.

When you write this: "if you want to get the plain list, you don't pass anything into the observers table", do you mean by list the whole table? Further, what is "observers table."? The way I am doing it now is like this

  useEffect(() => {
    const subscription = database
      .get('data_table')
      .query()
      .observeWithColumns(['col1_1', 'col_2'])
      .subscribe(dates => {
        console.log('It is changing because of col_1 or col_2', dates);
      });

    return () => {
      subscription.unsubscribe();
    };
  }, []);

This gets executed every time something changes in the table.

alicja-mruk commented 1 year ago

@Stophface Did you wrap your App component with DatabaseProvider?

Stophface commented 1 year ago

@alicja99 Yep...

Stophface commented 1 year ago

@alicja99 Apparently I was wrong. This

  useEffect(() => {
    const subscription = database
      .get('data_table')
      .query()
      .observeWithColumns(['col1_1', 'col_2'])
      .subscribe(dates => {
        console.log('It is changing because of col_1 or col_2', dates);
      });

    return () => {
      subscription.unsubscribe();
    };
  }, []);

does not seem to work. I am really stuck here.

KrisLau commented 1 year ago

@Stophface Just change your .observe to .observeWithColumns. Documentation: https://watermelondb.dev/Components.html?search=#advanced-observing-sorted-lists

Stophface commented 1 year ago

@KrisLau that does not change it.

const Foo = ({data}) => {
    console.log(data);
    return <SomeCoolUiWithData />
}

export default compose(
  withDatabase,
  withObservables(['data'], ({database}) => {
    return {
      data: database.get('data_table').query().observeWithColumns(),
    };
  }),
)(Foo);

is not doing anything...

KrisLau commented 1 year ago

@Stophface Read the documentation. You are passing nothing to observeWithColumns. It needs the array of column names to observe. From the docs:

withObservables(['post'], ({ post }) => ({
  comments: post.comments.observeWithColumns(['likes'])
}))
Stophface commented 1 year ago

@KrisLau I am doing that. This

export default compose(
  withDatabase,
  withObservables(['date_table'], ({dateTable}) => {
    return {
      whatever: dateTable.observeWithColumns([DATE, COUNT, YEAR]),
    };
  }),
)(FooBar);

Gives me

TypeError: undefined is not an object (evaluating 'dateTable.observeWithColumns')

My model looks like this

export class Dates extends Model {
  static table = 'date_table';

  @field(DATE)
  date;

  @field(COUNT)
  count;

  @field(YEAR)
  year;
}
KrisLau commented 1 year ago

The error tells you your problem already. the observeWithColumns takes an array of strings and you're passing it a bunch of undefined objects. Also the @field should also be a string. Please go through the documentation thoroughly and pay attention to the details to fix your setup.

Stophface commented 1 year ago

@KrisLau These [DATE, COUNT, YEAR] are constants defined like this

export const COUNT = 'count';
export const DATE = 'date';
export const YEAR = 'year';

I also can hardcode them in there, I am getting the same error...

KrisLau commented 1 year ago

@Stophface You're also not querying anything so observeWithColumns is just getting your entire table object instead of the results:

export default compose(
  withDatabase,
  withObservables([], ({database}) => {
    return {
      whatever: database.get('date_table').query().observeWithColumns(['date', 'count', 'year']), 
    };
  }),
)(FooBar);
Stophface commented 1 year ago

@KrisLau I think the await is not needed in there. However, this

export default compose(
  withDatabase,
  withObservables([], ({database}) => {
    return {
      whatever: database
        .get('date_table')
        .query()
        .observeWithColumns(['date', 'count', 'year']),
    };
  }),
)(FooBar);

gives me

TypeError: null is not an object (evaluating 'database.get('date_table').query')

KrisLau commented 1 year ago

@Stophface My bad, yeah no await. It looks like your DB setup is messed up.

I suggest you to try either:

Stophface commented 1 year ago

I went through the documentation a few times already. Everything works, exept this... The only thing that is different from the setup is that I have JSI disabled.

KrisLau commented 1 year ago

@Stophface Sorry I really have no idea how to help you, all I can say is that withObservables definitely works in my project. Like I said the only other thing I can think of for you is testing by creating a new minimal project (one screen, one db table)