How do I make a React Subscription function reactive? - mongodb

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

Related

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
});
}

Lifecycle for controlled textinputs

I was wondering if the mobx cycle is triggered on observable change only in stores outside of react components in HOClass operation
because there is a top level Provider to mobx stores which is triggering a react component re-render.
ReactDOM.render(<Provider { ...stores } >
<Routes />
</Provider>,
But does that mean I can't add observables directly to react components?
if so that is a bit unfortunate.
#observer
export default class Profile extends React.Component {
#observable name;
componentWillMount() {
this.name = this.props.name;
}
txtChange = (e) => {
this.name = e.target.value;
}
render() {
return (
<div>
<input type="text" onChange={this.txtChange} value={this.name}/>
</div>
)
}
for some reason on my app I cannot update the observable name. On some apps using mobx however I can. Why am I unable to change the value of this and trigger a react state update if the observable is attached to the React.Component itself?
Here I have forms updating in controlled observable fashion but when Provider 'ing my router on my current project under this setup no react rerender is triggered.
https://codepen.io/cheapsteak/pen/wzdvzQ
Help anyone?
There (was) a typo: the function is called txtChange but the component references this.txthange instead. Either way, here's a fixed working example: https://codepen.io/justnobody/pen/RxJmmY?editors=1010

Meteor subscription ready

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/>
}

Is it fine to mutate attributes of React-controlled DOM elements directly?

I'd like to use headroom.js with React. Headroom.js docs say:
At it's most basic headroom.js simply adds and removes CSS classes from an element in response to a scroll event.
Would it be fine to use it directly with elements controlled by React? I know that React fails badly when the DOM structure is mutated, but modifying just attributes should be fine. Is this really so? Could you show me some place in official documentation saying that it's recommended or not?
Side note: I know about react-headroom, but I'd like to use the original headroom.js instead.
EDIT: I just tried it, and it seems to work. I still don't know if it will be a good idea on the long run.
If React tries to reconcile any of the attributes you change, things will break. Here's an example:
class Application extends React.Component {
constructor() {
super();
this.state = {
classes: ["blue", "bold"]
}
}
componentDidMount() {
setTimeout(() => {
console.log("modifying state");
this.setState({
classes: this.state.classes.concat(["big"])
});
}, 2000)
}
render() {
return (
<div id="test" className={this.state.classes.join(" ")}>Hello!</div>
)
}
}
ReactDOM.render(<Application />, document.getElementById("app"), () => {
setTimeout(() => {
console.log("Adding a class manually");
const el = document.getElementById("test");
if (el.classList)
el.classList.add("grayBg");
else
el.className += ' grayBg';
}, 1000)
});
And here's the demo: https://jsbin.com/fadubo/edit?js,output
We start off with a component that has the classes blue and bold based on its state. After a second, we add the grayBg class without using React. After another second, the component sets its state so that the component has the classes blue, bold, and big, and the grayBg class is lost.
Since the DOM reconciliation strategy is a black box, it's difficult to say, "Okay, my use case will work as long as React doesn't define any classes." For example, React might decide it's better to use innerHTML to apply a large list of changes rather than setting attributes individually.
In general, if you need to do manual DOM manipulation of a React component, the best strategy is to wrap the manual operation or plugin in its own component that it can 100% control. See this post on Wrapping DOM Libs for one such example.