Making higher order components observers - Mobx React - javascript

I have an application with three main components. Form allows users to select items, Preview shows those items, AppStore is a Mobx store. The button seems to work fine, adding items to the store but the Preview component isn't re-rendering in response to changes. I think this might be because I'm using a higher order component for my drag and drop feature. Does anyone know anything about getting these to work with Mobx?
The project can be found here (the only buttons I've linked up at the moment are 'Banner Image' which adds an item and 'Remove' which should remove it): https://codesandbox.io/s/vnooqvn8yy

I think the issue is that SortableList needs to be passed a regular javascript array, not an observable array.
<SortableList items={AppStore.currentBlocks.slice()} onSortEnd={this.onSortEnd} />
From the mobx docs:
...whenever you need to pass an observable array to an external library, it is a good idea to create a shallow copy before passing it to other libraries or built-in functions (which is good practice anyway) by using array.slice()

Related

Update UI on Data change

(I mostly work with Vue.js so many examples will relate to it)
I am learning to build web apps and the most time-consuming thing is the synchronization of the UI components and the data that they should represent, that is, coherence between what is shown to the user and what is stored in memory (locally).
For instance, suppose I have the following component tree:
<body>
<form/>
<task-bar/>
<component-1>
<component-2>
</body>
Here the parent component is body, and it has four childs, namely form, task-bar, component-1 and component-2.
We have a list of objects, for example:
[
{
id: 1,
title: "X"
},
{
id: 2,
title: "Y"
}
]
Taskbar lists the component title.
Component-1 shows the object selected in Component-2 via a radio button.
Now, in form I want to add an object to the list or edit/delete the selected component in Component-2.
In Vue.js I would pass the list from parent body to its child components, however, parent and siblings are not reactive by default to the data of child and siblings. Therefore a change on the list by a sibling creates an incoherent state between all components.
I read two ways to solve it, first is using events, child does something and notifies (This is what I used to do back in Java Swing, an observer pattern), this however turn to be extremely cumbersome after some events, moreover, its hard to debug and you might repeat events code in different components.
The second way is using a global state, such as Flux pattern implemented in Vuex. This however couples all components to the Vuex of a the specific project and makes it less portable.
My question is, are these the two unique ways to do things? Am I doing it the wrong way?
You could also do one of the following:
use a variable like it was a global state. You have an example of this in Vue's doc: https://v2.vuejs.org/v2/guide/state-management.html
in your component, use $parent.doSomething(), where doSomething() is a method of your parent component.
But personally, I like vuex :)

Keeping timer in sync across components

Bit of background: I'm trying to learn a React and am building a small app with next (using a template from a tutorial I did a while ago). I've recently run into an issue where I need to keep a timer in synch between 2 components that are not in the same hierarchical structure.
I have an Item component that displays a few details about that item(description, title, etc) in a more concise way (a tile displaying just part of the entire item info). If I click on the Item tile, i have a next.js Link component that looks like this:
<Link href={{pathname: "/item", query: { id: item.id }}}>
<a>{item.title}</a>
</Link>
which will redirect me to a new page containing an ItemDetails component which just displays all the information for that item in more detail and using the entire page instead of just the tile used for the Item.
As mentioned above, the issue is that I want to have a countdown timer that is kept in sync between Item and ItemDetails (can be started, stopped, etc from either of them and it's state would be reflected in the other component). Initially, I thought of using MobX and creating a store and using that as a way to keep the same state between the two components. However, the problem with that is that I have multiple instances of the Item component, each pointing to their own ItemDetails and by using a single store that would just share the state of the first started timer between all Items (might be wrong here though since MobX is something i just started reading about yesterday ^^).
My question is, what would be the best way to approach this issue? Is this doable using Mobx plus stores or is it an issue of how the app is structured (eg: find a way to make Item and ItemDetails part of the same hierarchical structure?
Any help would be appreciated.
Here's a working example using React "context" to share the data between components and refs to keep time across re-renders:
https://codesandbox.io/s/silly-http-h25dr?fontsize=14&hidenavigation=1&theme=dark

How to speed up contacts handling in react native when slow?

I have been working on putting together a module to allow a user to invite their friends to use an app. Code works without major issue, but since I have over 100 contacts in my phone, the speed is rather slow. Scrolling isn't a problem, and I can add a loader as the phone pulls the data. But I when I choose an element for highlight it takes a few seconds for the item to get checked. I'm curious if anyone has any tips for how to optimize?
My snack is below:
https://snack.expo.io/#fauslyfox110/testingreferrals
Main file: inviteScreenTwo.js
React will update the elements on screen whenever you make it change to your state. In your case, I suspect that the delay is due to React going through all the contact records your showing and updating them when you change the highlighting.
One way of dealing with this is to make sure that contacts that are off-screen aren't actually in the DOM. You would need to update your render method to place only contact records in the list that are actually visible. That way, React won't need to update as many elements. Please refer to the React docs to read more about this optimization.
Another way would be to override the lifecycle method shouldComponentUpdate for your record components, making sure that the only rerender when their highlighted status changes. This is called reconciliation avoidance. The method has the following signature:
shouldComponentUpdate(nextProps, nextState) {
}
By default, this method always returns true. You could change it to compare nextProps with this.props, checking that the highlighting has changed, and return true or false as appropriate. Read more about this optimization in the React docs.

vuejs accesing events and data between multiple components

I have an application that displays a google map with places autocomplete controller added to it just like this example from google
when an address is searched and selected, or the map bounds are changed I call algolia search, which has an event of onResult, that is fired when it received a response.
I am trying to turn this all into VUE js components. I have managed to get a google maps component and an autocomplete component.
I load the autocomplete first and then have the mounted section of the google maps attach it as a controller.
Where I start to fall down is the interoperability between the components.
I.E on place change which is an autocomplete event. I need to recentre the map and make the search.
But if they are two different components I can't get a reference to the google map.
when I bring the agolia search in to play, that also needs reference to the map when the event fires to pass the marker to it.
I started trying to use a simple view store, but this seems like I am tightly coupling the components.
Have I missed something or are simple stores and global event buses the way to go?
TL;DR;
Vuex may solve your problem, but need to see more code to know what's going on
There are multiple ways of achieving this, but I'll only list two.
Global State Management (Vuex), and props/listeners
Global State Management (Vuex)
If you know you'll only have one instance of each of the components (one map, one autocomplete) this is easy, fast, and reliable solution. The two components do not need to know about each other, they both deal with the global store. The autocomplete will update the data in the store, and the map will be notified whenever the variables it subscribes to change, and update accordingly.
The downside is...
Using vuex makes it harder to reuse and components.. Once you have more than one instance (ie. two autocompletes and two maps) then you may run into some issues, so you'll need to add additional complexity.
Props/Emit
If the two components have a direct connection, either siblings or parent-child relation, using this interaction (IMHO) is preferred.
The autocomplete component can have an #change or even a v-model set up, that parent component would link to the map component using a prop.
It seems like you may be doing it this way, which is not wrong, but without seeing any code, it's hard to make an assessment.

Ember component leaking state

I'm new to Ember and have a leaking state problem. I have a carousel widget that displays one item at a time and allows the user to click previous/next to see each item.
Here's the simplified carousel's component:
<button {{action "nextItem"}}>Next</button>
{{carousel-item item=selectedItem}}
Clicking next changes the selectedItem property so the next item is shown.
What I've realized is that the carousel-item component isn't re-initialized every time I move to a previous/next item. The DOM is reused each time, and the component's properties are shared since it's all one instance, which means I can have leaking state.
The alternative I see is to render all the items initially, so each has its own instance:
{{#each items as |item|}}
{{carousel-item item=item}}
{{/each}}
and to hide all but the selected item using CSS. However, this option kind of feels like a jQuery hack -- seems like Ember would have a better way. And I'm only ever showing one item at a time, so I hate to have so many extra DOM nodes when I don't need them.
What is the recommended way to handle this kind of a UI, where you only need one item shown at a time but don't want to share state between items? I'd imagine I should have one instance of the carousel-item component per item, instead of sharing an instance across all of them. But it doesn't feel right to instantiate every single carousel-item at first either. And I can't imagine the Ember way is to worry too much about the DOM details myself (determining which one is shown/hidden based on a class and some CSS).
Firstly, whatever framework or library you are using, jQuery, ember, angular, react, they are just a pack of JS/HTML/CSS right? So you should think in it's way, there is no magic!
So of course 1 component will only create 1 instance. If you just changed it's property(item in your demo), it just changed the property of an instance, other properties of it will remain as it is and triggered re-render. You cannot expect more. You have to manually reset other properties.
And yes, rendering everything by {{each}} looks stupid, but think about it, how could you create a smooth carousel animation by render only one DOM? At least you need to render 3 (current, previous and next) right?
Since carousel is a common UI, I recommend you to check existing ember addons fist before you write by yourself: https://emberobserver.com/?query=carousel
If I understood your problem correctly, the willUpdate hook in Ember.Component class should help you out. I this hook you can clear up the attributes, remove DOM objects, or anything at all. This will be called each time the component is about to re-render itself.
A simple example is of form:
willUpdate() {
Ember.$(this.get('element')).empty();
},
This will clear the DOM on each re-render forcing it to redraw elements.
You can try out other hooks too and see which event will serve your need. All of them are very helpful and serve different purpose.

Categories

Resources