(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 :)
Related
I’m trying to fire a callback passed down from a parent component. Our old pattern for handling this was to call the function in didInsertElement. In Octane, I see that we can use the did-insert modifier but that seems weird for this use case since we’re not updating the DOM element that we’d use to call did-insert. I've also seen onRender being used in a few cases but I don’t see documentation on that and it’s not firing for me. Any suggestions?
For this specific use case, we have a parent component that can have one of many child components. And for each child component we have specific text that gets displayed in the parent component and we want the child component to be the owner of that text.
Your instinct that did-insert isn't the right solution here is, I think, correct. In general, modifiers should only be used when the element they're going to be attached to is used in some way—that is, for managing interactions with the DOM. In general, we prefer one-way data flow otherwise. However, the scenario you've outlined looks similar to a "registration" pattern, where when a child is instantiated, it tells its parent "Hey, I'm here, here's the info you need about me."
There are a couple reasonable options in this case:
Rethink whether the child component should in fact own that data. I don't doubt you have a good reason for the child component owning the data, but the fact that the parent is already responsible for deciding which child to render may suggest that a mapping which includes both the component to render and the associated text could be a good solution in this space. That would cleanly solve this issue.
Given that per your description you currently do want to avoid having the parent own that data, you could also consider having the child yield the data. However, this usually only works if the DOM relationship for it makes sense. If it does, you could choose to do something like this:
{{yield (hash block='data' text=this.theText)}}
<div class='the-component-body'>
{{yield}}
</div>
<ChildComponent as |child|>
{{#if (eq child.block 'data'}}
<h2>{{child.text}}</h2>
{{/if}}
{{child}}
</Child>
(You can see this strategy working here—in particular, see the resulting DOM!)
While that's very powerful, again, it only works if your DOM layout supports it.
Finally, and perhaps most simply for the use case you have, you can (though not necessarily best, as I think the other options above are usually better when available), you can 'register' the value for the component by calling an action passed into your component during the constructor. Now, using a component's constructor this way for component behavior can be a problem in that it only runs once, when the component is first instantiated, and Glimmer and Ember keep the component instance stable and just change the values passed to them over time as much as possible, so if the value you're passing back up depends on the argument you pass to it, the constructor won't work. If it's always stable and does not depend on the arguments to a component, this does work, and it's often appropriate for a 'registration' pattern, where the child component simply needs to pass a single piece of data to the parent when instantiated.
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()
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.
In my flux application, I have a DropDown React component that renders itself with an array of Key Value Pairs.
I'd like to have two different drop downs, one with Country Data, and another with City Data.
In the Flux pattern, each dropdown would have a Selection Action that contains a payload of the selected value, which a corresponding store would use to update it's state.
How do I specify which dropdown Selection action belongs to which store?
I can create a wrapper component that is specific to each need i.e. CountryDropDown and CityDropDown, and have each create their own specific action CountrySelected and CitySelected but is that the idiomatic approach? If it is, how do I wire up the underlying DropDown component so that it's onChange handler fires the parent's action?
Actions should not belong exclusively to one store or another. This is very much like creating a setter method in the store, which is antithetical to Flux.
One of the central ideas behind Flux is that all stores are informed by all actions. The dispatcher is the mechanism by which actions are distributed to all the stores. This keeps the flow of data open to changing needs.
There are probably a few different solutions to your problem.
I would consider adding a selectedType field to the action that is either 'city' or 'country' (or you could use constants instead of strings). You could pass this value into the React component as a prop, if you are trying to keep it abstracted.
Likewise, if you would rather have completely flexible control over the behavior of the child, and you want to define that in the parent (your final question above), you can pass a callback to the child component as a prop.
You could have separate actions dedicated to each type, as you described, but that seems like duplicating code to me.
Say I've defined a <Tag> component that has two props: id and name. My application renders this component in two different places, so I have a <Tag> component whose id is 1 appearing twice, but whose owners are different. One tag is owned by an <Entries> component, and the other is owned by a <Tags> component.
When a <Tag> is clicked, I want to change its state, and I want all other <Tag> components with the same id to also change their state so that they match. What's the best way to do this? Should all <Tags> have the same root component? I don't like that idea, because I feel like it's a brittle solution.
I think you have two options to go:
1- React way
You should keep the state in a common owner component of <Tags> and <Entries>.
You should pass a change handler from the state owner component to the <Tag> component
When the tag changes the state owner will update <Tag>s in both <Tags> and <Entries>
Reading this should give you a lot of insight:
http://facebook.github.io/react/docs/thinking-in-react.html
2- Pure Flux way
If your application is a little more complex than what you have told you can go with flux architecture.
To do this you should have actions,dispatchers and stores implemented in your application.
When a <Tag> is changed you call an action, which in turn calls a dispatcher, which is in turn listened by stores, which in turn emits a change event to components. Then all the components updates themselves with the new state.
You can find about flux here:
http://facebook.github.io/flux/docs/overview.html
id's should generally be unique within an html document.
The best solution would be to change your id's to classes.