For my first React app I need to display a menu hiearchy with group-items, read-items and write-items (there are more, but that's enough for this example).
Each read-item and write-item are connected to a data point in a JSON API: each item has a resource (for example /api/1.0/fruits) and a json-path (for example data.color).
When the menu is displayed all values should be fetched from the web service. But I don't want each item to fetch its value independently because then the same resource would be read multiple times in the typical case.
I have a static, stateless menu structure as well that the views are built from. I can call a method, getRequiredResources(), on the root menu item in this menu structure which will return a set of resources.
But then I've introduced dynamic menu items, so that depending on the state of the menu items different resources are required. I can no longer use the static menu structure to collect all required resources, since it has no knowledge about the state of each item.
Any hints on how to handle this? If I could access the child menu item components they hold enough state to return a list of required resources, but I don't think that's a recommended pattern...?
I use a Flux architecture for data flow.
It is good practice to maintain state as high as possible in the component tree. Best solution in your case depends on the source of the dynamic conditions of the menu structure:
If these dynamic conditions originate from the outside world (e.g. whether or not user is logged in):
Put state inside the top menu component, including all the information about the condition of its child components.
Then the top component could still fetch all data, and render the dynamic menu.
This maintains your requirement to fetch menu data only once.
For this you to maintain info about the condition of menu somewhere in a store, to pass on to top component.
If the dynamic conditions originate from within the menu item (e.g. a checkbox or filter for subitems inside the menu)
Give the child components state, and have each child fetch their own stuff based on that state. (Start with loading state, fetch items, and re-render).
In that case, you may be fetching same data twice (since children do not communicate with each other.
If the dynamic conditions originate from within the menu components and your dataset is not overly large or complex (sounds like this doesn't fit your needs):
you could still fetch everything at the top, and use state inside the children only for filtering purposes.
If dynamic conditions originate from within the menu, but you want to save the state somewhere (e.g. for when user revisits page):
then keep state in top component, as well as all dynamic children conditions.
Children are all stateless, everthing is passed as props.
Whenever inside a child a filter or similar is set, that child fires an action to the dispatcher. You then need to save your filter conditions also somewhere in a store.
Related
(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 :)
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
I have a React component that renders a list of items. I have a different component which allows user to add a new item. This new item is created by making an async call to the server. Can the component that renders the list listen to changes in the server and update itself.
The new item is added through a modal hence when the modal is closed, an action needs to be triggered to update the component that renders the list of items. Right now I am having to refresh the browser to reflect the changes in the UI.
If I close my modal, there is no way I am triggering a change to the component that renders the list of items. I thought about adding the result of the async call which is a list item to the state using redux as a way to cause a re-render. However, the client I am interfacing with does not give back all the properties that the items requires (as a part of the Typescript interface). Hence this method won't work. An alternate way to force a re-render to the component that displays the list. But again, can't really think of a way that would be work and how would I trigger the componentDidUpdate hook.
if (the server supports webSockets) {
use webSockets;
} else {
use socket.io;
}
I'm doing an implementation in which I need to detect when all children components have been loaded, from a multi-level hierarchy.
Example: MAIN component have a WORKSPACE component, which can have multiple COLUMN components and each COLUMN can have multiple WIDGET components.
Each widget is rendered after ajax calls (have to fetch some data in the server).
I need to detect when all WIDGETs components have been loaded (after async calls) in MAIN component.
I do not know which is the best approach/pattern to do this.
I know I can use some callbacks in each component to tell the parent that the component is loaded and then do this recursively, but I'm looking for a solution that solves this without having to deal with each level of the hierarchy.
Is it possible?
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.