Meteor subscription ready - mongodb

I'am building a react/meteor app. I'm having a problem with subscriptions. There's a component that is showed when the subscriotion.ready() is false. When it's turned to true the component is replaced by a table with data, but it takes a few seconds between the ready and the data from find().fetch(), showing another component for a while.
Any suggestion ?
Thanks

If you are using react-meteor-data you can get the subscription status in ready property. Then you can send this property to the presentation component and update it accordingly.
Sample code snippet from the package's documentation:
import { createContainer } from 'meteor/react-meteor-data';
export default PresenterContainer = createContainer(props => {
// Do all your reactive data access in this method.
// Note that this subscription will get cleaned up when your component is unmounted
const handle = Meteor.subscribe('publication_name');
return {
isReady: ! handle.ready(),
list: CollectionName.find().fetch(),
};
}, PresenterComponent);
Explanation:
The first argument to createContainer is a reactive function that will get re-run whenever its reactive inputs change.
The PresenterComponent component will receive {isReady, list} as props. So, you can render your component according to the status of isReady
Addition:
Write your render method of the presenter component like this:
render(){
if(!this.isReady) return <LoadingComponent/>
else if(this.props.list.length() != 0) return <TableComponent/>
else return <NoDataFoundComponent/>
}

Related

Update global state after RTK Query loads data

I've noticed a problem with splitting responsibilities in React components based on the fetched data using RTK Query.
Basically, I have two components like HomePage and NavigationComponent.
On HomePage I'd like to fetch the information about the user so that I can modify NavigationComponent accordingly.
What I do inside HomePage:
import { setNavigationMode } from "features/nav/navSlice";
export default function HomePage() {
const {data: user} = useGetUserDataQuery();
const dispatch = useAppDispatch();
const navMode = user ? "all-options" : "none";
dispatch(setNavigationMode(navMode)); // here I change the default Navigation mode
return <MainLayout>
<Navigation/>
<Content/>
<Footer/>
</MainLayout>;
}
The HomePage is a special Page when the NavigationComponent shouldn't display any options for the not logged in user.
Other pages presents additional Logo and Title on Nav.
React communicates:
Warning: Cannot update a component (NavComponent) while rendering a different component (HomePage). To locate the bad setState() call inside HomePage, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
Not sure what is the right way to follow.
Whether the state should be changed in GetUser query after it is loaded - that doesn't seem to be legit.
problem is dispatch calls every render. Instead you can create a navigationSlice (if you don't have already) and use extraReducers for matching your authorization action like:
extraReducers: (builder) => {
builder.addMatcher(
usersApi.endpoints.login.matchFulfilled,
(state, { payload }) => {
if (payload.user) {
state.navigationMode = "all-options"
}
}
);
}
This way, state.navigationMode will only change when authorization changes
The solution was too obvious. The dispatch should be run in useEffect.
import { setNavigationMode } from "features/nav/navSlice";
export default function HomePage() {
const {data: user} = useGetUserDataQuery();
const dispatch = useAppDispatch();
const navMode = user ? "all-options" : "none";
// changed lines
useEffect( () => {
dispatch(setNavMode(navMode));
}, [navMode, dispatch]);
// /changed lines
return <MainLayout>
<Navigation/>
<Content/>
<Footer/>
</MainLayout>;
}
Thank you #papa-xvii for the hint with changing the navMode after user login. That solves the second problem I had.
However I cannot accept the answer as it does not solve the problem I described above.

Are ngXS selectors fired on the page init, without any changes of the state slice that returned?

I have a selector and I subscribe to them on ngOnInit; but the code inside the subscribe is executed every time when the page is initialized (refreshed).
#Select(SurveysSelectors.deleteSurveys) deleteSurveys$: Observable<IDeleteSurveys>;
.
.
.
ngOnInit(): void {
this.deleteSurveys$.pipe(takeUntil(this.destroy$), debounceTime(600)).subscribe((result: IDeleteSurveys) => {
if (!result.surveyDeleteResult.esriUpdate) {
return;
}
this.esriUpdate(result.surveyIds, result.surveyDeleteResult.iotFunc);
});
}
Is this normal? I expected that the code inside subscribe to run only when a change is made on the slice of state that selector returns.
this is expected since your ngOnInit will run every time your component is initialized. In NGXS the selectors are hot and always will emit on subscription the last value, in this case the initial value. A possible workaround would be to use a skip(1) to be sure you only react to changes that happen after you subscribed.
Example below:
this.deleteSurveys$.pipe(takeUntil(this.destroy$), skip(1), debounceTime(600)).subscribe((result: IDeleteSurveys) => {
if (!result.surveyDeleteResult.esriUpdate) {
return;
}
this.esriUpdate(result.surveyIds, result.surveyDeleteResult.iotFunc);
});
I hope this can help you.

LitElement with data from Firestore

I've been trying to dynamically insert data from Firestore into my component.
Currently, I'm using the firstUpdated() lifecycle. My code works but it fell like there's a better way of doing this.
This is my current component.
static get properties() {
return {
firebaseData: {type:Object},
}
}
constructor() {
super()
this.firebaseData = {}
}
firstUpdated() {
firestore.doc(`...`).get()
.then(doc => {this.firebaseData = doc.data()})
})
.catch(err => console.error(err))
}
render() {
return html `${firebaseData.title}`
}
I was hope someone with more experience would be open to sharing their knowledge. Thanks in advance!
firstUpdated should be used when you need to interact with shadow DOM elements inside your web component, as they aren't created until then. It's the earliest moment when you can be sure your component DOM exists.
I would prefer to do the firebase call earlier, even in the constructor.
The idea is, your firebase call isn't dependent of the rendering, so you could directly do it at the earliest moment, and as in the callback of the function you update the firebaseData property, a new rendering cycle will be done then.

Events provider is deprecating. Using Redux or Observables for state in ionic apps

I've been using events in my ionic application, where i subscribe in one page, and publish the event in the other page. Now I see a warning that Events are going to be changed with Observables and Redux state and effect.
I was using Events mainly to call for component function changes outside it, so I had a components for example:
Component1.ts
this.events.subscribe('event:addValue1', (data: any) => {
this.valueName = 'VALUE1';
});
this.events.subscribe('event:addValue2', (data: any) => {
this.valueName = 'VALUE2';
});
and than outside this component I was calling the publish methods from any page, like:
Page1.ts
this.events.publish('event:addValue1');
Page2.ts
this.events.publish('event:addValue2');
By this i was able to change the data (this.valueName) outside the Component1.ts from any other page, simply by publishing the desired event.
I know that this might not sound or be right approach, but It was the only way I was doing changes to my Component1.ts outside it from any page.
I have now changed this and just put separate functions and than i access them via ViewChild component name like
#ViewChild('component') component: any;
....
this.component.functionAddValue1().
and additionally I send additional params via Angular NavigationExtras if i need to calculate and call some function from the Component1.ts, lets say if I navigate to some route.
Before this I was just calling the events.publish and I was able to make the changes to the Component1.ts on the fly.
Create event service.
In the EventService.ts:
export class EventService {
private dataObserved = new BehaviorSubject<any>('');
currentEvent = this.dataObserved.asObservable();
constructo(){}
publish(param):void {
this.dataObserved.next(param);
}
}
For publishing the event from example page1:
constructor(public eventService:EventService){}
updatePost(value){
this.eventService.publish({name:'post:updated',params:value});
}
In page 2:
constructor(public eventService:EventService){
eventService.currentEvent.subscribe(value=>{
if(value.name=='post:updated'){
//get value.name
}else if(value.name=='another:event'){
//get value or update view or trigger function or method...
}
// here you can get the value or do whatever you want
});
}

How do I make a React Subscription function reactive?

I'm trying to implement Pagination for my Meteor App using React and mongo. I've done this by passing a limit prop to my subscription function like so:
export default class BookListTable extends TrackerReact(React.Component) {
constructor(props) {
super(props);
var limit = this.props.LimitProp
limit = parseInt(limit) || 5;
this.state = {
subscription: {
booksData: Meteor.subscribe("allBooks", {limit: limit})
}
}
///// rest of component
This works great the first time the react component renders but when I update the props nothing changes. I expect the component to re-render with the updated limit property - however this doesn't happen. What am I missing?
Any related info around pagination appreciated!
When you update the property LimitProp, the component re-renders with LimitProp changed but the constructor is not invoked again. You only copy the value of the LimitProp to limit and then use it when the component is created, so the state (suscription) is not updated when it changes. I think that you should use componentDidMount.
Component Specs and Lifecycle