Observe every rendering (even conditionals) in EmberJS - javascript

I want to get a function running after any rendering in EmberJS has happened (globally), especially after the "switch" in an {{#if}}-{{else}}-block.
Subscribing to the render event with
Ember.subscribe("render", {
before: function (name, timestamp, payload) {
},
after: function (name, timestamp, payload) {
// function call
}
});
works quite well but is not called after the rerendering via the if-else-switch.
So what I want to do is listen to the rendering globally and be noticed at the end of it.
Best regards
Martin

Related

React Redux - quick flash of previous state before dispatch issue

I'm building a React + Redux app with a Node.js backend, and one of the features is that a user can view profiles of other users. To do this, I have a section in the Redux state called users that looks like:
{
...,
users: {
user: {},
isLoading: true
}
}
Every time the /users/:id route is rendered, a getUser(id) action is dispatched and fills the state with the received information.
The main issue is when a user views user1's profile page (therefore redux state is now filled with user1's profile) and then views user2's profile page, the dispatch getUser(2) action is called right after the page is rendered. Since user1's info is still in the state, it will flash their info for a very short time, and then show the loading spinner until the new user is loaded.
I read about dispatching a resetUser(id) action on every unmount of the page, but I'm not sure if this is the right way to go. Also, one of the features is if a user is viewing their own page, they have an edit button which redirects them to /edit-profile. If I reset the state on every unmount, I'll have to fetch their profile again (in the edit page), even though I just did that when they viewed their page.. And that doesn't seem like it makes sense.
Any ideas how to solve this? Thanks!
The render phase runs after mounting. And you stated that previous data is being shown before new data. It seems that you have asynchronous mounting:
async componentDidMount() {}
It will continue rendering even before mounting phase is completed. To avoid issue, you may use synchronous nature of mounting:
componentDidMount(){}
Where you'll call api data.
Now, when you reach to rendering phase it will have new data available before rendering and won't flash you old data.
You now may be wondering how to call api asynchronously. You can create a asynchronous function and call that function inside the synchronous componentDidMount.
componentDidMount() {
yourAsyncFunc()
}
async yourAsyncFunc() {} // api call
How can I do this with React hooks?
While using useEffect, don't implement async:
useEffect(async () =>
Implement it simply:
useEffect(() => {
// you can still use asynchronous function here
async function callApi() {}
callApi()
}, []) // [] to run in similar to componentDidMount
If you miss second parameter to useEffect then you are not ensuring it to run on mounting phase. It will run before, after, and in the rendering phase depending on case.
Implementing something like resetUser(id) seems to be the right way here. I use this approach in my current project and this does solve the problem. The other approach of removing async keyword from useEffect callback as mentioned in another answer didn't work for me (I use hooks, redux-toolkit, Typescript).
After dispatching this action, your state should look something like
{
...,
users: {
user: null,
isLoading: false,
}
}
If you are using hooks, you can dispatch the action this way:
useEffect(() => {
const ac = new AbortController();
...
return () => {
dispatch(resetUser(null));
ac.abort();
};
}, []);
Action could look something like this:
resetListingDetails(state, action) {
// Immer-like mutable update
state.users = {
...state.users,
user: null,
};
}

How to deal with race conditions in event listeners and shared state?

I have 2 event listeners that operate on the same shared data/state. For instance:
let sharedState = {
username: 'Bob',
isOnline: false,
};
emitter.on('friendStatus', (status) => {
sharedState.isOnline = status.isOnline;
});
emitter.on('friendData', (friend) => {
if (sharedState.isOnline) {
sharedState.username = friend.username;
}
});
My problem is that these events are emitted at any order. The friendData event might come in before the friendStatus. But friendData does something with the data returned from friendStatus. In other words: I need the event handler for friendData to execute after friendStatus, but I don't have this assurance from the event emitter perspective. I need to somehow implement this in my code.
Now of course I could simply remove the if (sharedState.isOnline) { from the friendData listener and let it run its course. Then I'd have a function run after both handlers have finished and somewhat reconciliate the shared state dependencies:
emitter.on('friendStatus', (status) => {
sharedState.isOnline = status.isOnline;
reconcileStateBetweenUsernameAndIsOnline();
});
emitter.on('friendData', (friend) => {
sharedState.username = friend.username;
reconcileStateBetweenUsernameAndIsOnline();
});
Problem is that this reconciliation function knows about this specific data dependencies use case; hence cannot be very generic. With large interconnected data dependencies this seems a lot harder to achieve. For instance I am already dealing with other subscriptions and other data dependencies and my reconciliation function is becoming quite large and complicated.
My question is: is there a better way to model this? For instance if I had the assurance that the handlers would run in a specific order I wouldn't have this issue.
EDIT: expected behavior is to use the sharedState and render a UI where I want the username to show ONLY if the status isOnline is true.
From #Bergi's answer in the comments the solution I was hinting seems to be the most appropriate for such case. Simply let the event-handlers set their own independent state, then observe on the values changing and write appropriate logic based on what you need to do. For instance I need to show a username; this function shouldn't care about the order or have any knowledge of time: it should simply check whether the isOnline status is true and if there's a username. Then the observable pattern can be used to call this function whenever each dependency of the function changes. In this case the function depends on status.isOnline and friend.username hence it will observe and re-execute whenever those values change.
function showUsername() {
if (status.isOnline && friend.username != '') return true;
}
This function must observe the properties it depends on (status.isOnline and friend.username). You can have a look at RxJS or other libraries for achieving this in a more "standard" way.

Meteor Tracker not receiving subscriptions automatically

I'm creating some templates using Template.onCreated, and am then using Tracker.autorun to make some subscriptions and then collect data from the Server and store them on the client's MiniMongo.
Then, I can leave said Template and eventually route myself back. However, I'm noticing that upon coming back, Blaze is not capturing my changes. When routing back, it seems that onRendered is called instead of onCreated. This makes sense, however, onRendered isn't quite reacting to all my data changes yet. I've noticed it's because minimongo doesn't actually have data in it for onRendered.
However, I've noticed it can get the object by refreshing the page (thus calling onCreated and subscribing again and getting a fresh set of data from the server) My workaround is to re-subscribe in onRendered, but I feel like i shouldn't have to do this because I already initially subscribed in OnCreated. Is this an issue with Meteor, is DDP just going slow?
Let's say I have some default generic collection called Objects.
const Objects = new Mongo.Collection("Objects");
So in my onCreated I subscribe to a collection as follows
Template.foo.onCreated(function () {
var self = this;
self.autorun(function () {
const objId= FlowRouter.getParam("id");
self.objectSubscription = Meteor.subscribe('obj', objId, {
onReady: function () {
},
onStop: function (error) {
if (error) {
alert('error')
}
}
});
Then in onRendered I have to hack it and unnecessarily re-subscribe because I don't have objId i.e. I can't just use Objects.find(),
Template.foo.onRendered(function () {
const objId= FlowRouter.getParam("id");
Meteor.subscribe('obj', objId, {
onReady: function () {
$('#obj').addClass('highlight');
},
onStop: function (error) {
if (error) {
alert('error');
}
}
});
//I feel like I should just be able to do
//const obj = Objects.findOne({_id:objId})
//$('#obj').addClass('highlight')
});
Any insights, why would I be losing data in minimongo? And is it bad practice to "re-subscribe"?
onCreated and onRendered aren't reactive, but template helpers are. So, to constantly detect changes in those two you'd have to create a reactive context and place it inside them which can be achieved using Tracker.autorun. Also, be advised that it's better to keep onRenderd for DOM manipulations and onCreated for subscriptions and creation of reactive variables.
You don't need double subscriptions to detect changes and IMO this is generally a bad practice.
Template.foo.onCreated(function onCreated() {
const self = this;
this.autorun(function() {
const id = FlowRouter.getParam('_id');
self.subscribe('obj', id);
});
});
Template.foo.onRendered(function onRendered() {
const self = this;
// this will run after subscribe completes sending records to client
if (self.subscriptionsReady()) {
// do whatever you want
$('#obj').addClass('highlight');
}
});

How to workaround too often Redux updates in a React application?

I have a browser application with the React-Redux architecture. Many events occur in the application (e.g. timeouts, web socket incoming messages, web workers messages, XHR responses, etc.). Each event emits a Redux action that changes the Redux store that causes a React render.
The problem is that the events occur so often that React doesn't have enough time to render the UI (for example, an event occurs every 5ms (or more often) and React takes 10ms to render the UI). It makes the whole page stop responding because the render process doesn't stop (it always has something to render) and the browser has no time to draw the DOM or handle a DOM event.
What are the approaches or ready solutions to solve this problem considering that I can reduce neither the events frequency nor the React render time?
When you update the state from a websocket message, record the time and only do another update if a certain amount of time has passed since then.
In this example the state is updated only if 1000ms have passed since the last update:
client.onUpdate(data => {
if (!this.lastUpdate ||
new Date().getTime() - this.lastUpdate.getTime() > 1000) {
this.setState({ data });
this.lastUpdate = new Date();
}
})
You can simply debounce your handler.
debounced function means it can be invoked only once in X period of time.
For the sake of the example I will not implement the details of debouncing a function, but rather use lodash.
// function that handles the server update.
const serverUpdateHandler = (dispatch) => {
dispatch({
type: SERVER_UPDATE
})
}
// create a new function that is debounced. aka - will invoke after 250 milliseconds after the lace invokation
const debouncedHandler = _.debounce(serverUpdateHandler,250);
// pass socket.io the debounced function
socket.on('server-update',debouncedHandler)

Best way to wait for another action in flux

I'm using react and flux (facebook implementation). In my component I should do something like:
componentDidMount () {
UserActions.loadUser(this.props.username);
PublicationActions.findByUser(this.state.userId);
UserStore.on('CHANGE', this._setUser);
// ...
},
The problem is this.state.userId will be defined only when request triggered by UserActions.loadUser is finished.
So the only way of resolving this problem is putting something like:
if (this.state.userId && !this.state.publications) {
PublicationActions.findByUser(this.state.userId);
}
directly to render() method. I don't like this solution cause render() is for rendering and I don't want to mess any business logic in it.
The only acceptable solution I have found is have special component hierarchy for this case like:
<User username={someVar}>
<PublicationsList userId={this.state.userId}/>
</User>
This will enforce PublicationsList to be smart component (with state and so) but I'd like it to be dumb renderer. Is this the only way or there's something better?
You need store which is listening on UserAction.loadUser finish.
You invoke this action.
In your component you register callback which listening your new store.
When your store invoke your callback, then you have in this callback function access to userId from this store. (Im not sure but maybe your UserStore do wiat I write).
In this callback function you invoke
PublicationActions.findByUser(YourStore.getState().userId)
In render function you check if you have userId. If no then show some loading info. If tez then show normal rending data.
You could expand your loadUser method to accept callback with payload as arguments.
ie:
loadUser(username, callback) {
// some ajax stuff
callback(payload);
}
Then, in your jsx file you could write something like this:
componentDidMount () {
UserActions.loadUser(this.props.username, (user) => {
PublicationActions.findByUser(user.userId);
});
UserStore.on('CHANGE', this._setUser);
// ...
},

Categories

Resources