I'll mention in advanced that this is not a technical question on how to do the data update between 2 sibling components, rather what is the correct way to do so.
I have the following component tree structure:
App
|
-- Home
|
-- SearchBar
| |
| -- Filters
|
-- ItemsList
ItemsList has logic in it to load the list of items from an API call and show the list of items on the page. It also manages the state of the articles. If an article is deleted it removes it from the list and updates it's state.
SearchBar is a component that contains a textual search that is displayed above the list where a user can enter text to search and also has a button that opens the Filters component where the user can filter different parameters. Once the user search's or filters the list in the ItemsList component should be updated accordingly.
There are a few ways I can think to achieve this:
Using react context - The provider will be on the Home component (or another component for holding SearchBar and ItemsList - and both SearchBar and ItemsList will contain a consumer that will updated the state with a method in the provider - those updating the components. This in my opinion create some dependency between those components and they are not really standing on their own (the ItemsList component should be used in other pages as well - of course this is still possible, but yet does not feel so "clean").
The ItemsList component will contain public methods such as "delete item", and "clear list" and those methods will be called from the home component. The SearchBar will get a property with a function as an event - something like "onFilterChanged" and will call the method on ItemsList (I will need to hold a ref to that component). But working like that feels like each component can stand and be re-used on it's own merit - but loosing the "reactiveness" and more wire up that needs to be done.
Are there any other ways to achieve what I'm looking for that I'm not thinking of?
What is the correct way to architect this kind of solutions?
When two siblings need access to the same state, keeping the state in a mutual parent component is a common and recommended way of handling it (the official React documentation encourages that approach for most cases). That way, both the state and any methods to update the state can be passed to both children as needed, and any updates made by one child will be reflected in the other child.
There can be drawbacks to that approach: Depending on the size of the application, prop drilling can make code confusing and difficult to maintain—especially if one component that is using said data is being used in multiple places, deeply nested, or both.
For such occasions, holding the state in context or redux is a more appropriate approach. The first option that you listed is completely legitimate and what I would recommend. Maybe extracting your context entirely (not keeping it in your Home component and instead creating the context in its own file) would help things feel more "clean."
Determining when to use which approach is something that comes with time and experience. When choosing an approach, it is helpful to keep your future plans in mind. If you know the application will be small and simple, keeping the state in a parent component is a great move. If you have big plans for your application, using context or Redux from the start will be easiest.
Thankfully, the worst case scenario is that you decide to change from one approach to the other, which can always be done (confusing and tedious as it may be at times).
Related
Let's say that I'm fetching some images from an API in the App component.
Then I want to pass it to the component responsible to rendering images. But this component is not a direct child to the App component. It is the child of a direct child of App component.
Here's how I would pass the images array down to the image component, but I feel like it might not be the best approach.
But what would happen if this hierarchy gets more complex - even just by one more component:
Intuitively, it might not be the best thing.
So, what would be the best way to pass the images array down to the image component, if there are many other children between them?
The problem is usually called Prop Drilling in the React world: https://kentcdodds.com/blog/prop-drilling
A few options:
Passing props may not be that bad. If the app is small, is a very modular -and easy to unit test- option . You can reduce the verbosity by using prop spread: <Comp {...props} /> and by keeping your component interfaces similar. (many people dislike prop spreading as you can unintentionally pass unsupported props, but if you use TypeScript the compiler will catch that).
You can use a React Context: https://reactjs.org/docs/context.html (as other mention in the comments). However, keep an eye on how your context objects are defined (keep them small). React will re-render all the childs using the context when the value changes, is not smart enough to automatically detect changes at the property level. Frameworks like Redux or Zustand use other mechanisms to allow a granular control of the shared state (you'll see examples of a useSelector hook).
You can also take a look to a state management framework (Zustand is my favorite, but Redux is more popular). However, it may be an overkill for small things.
My personal choice is to start with prop drilling, it's easier to modularize and unit test. Then you can think on your app in layers: upper layers depend on a context (or a state framework), and lower layers receive properties. That helps when you want to refactor and move reusable components to other projects.
So im working on a React "Todo" list as a first React project, im using Rails as an API backend (strictly taking in and sending back .json), I am fairly familiar with Rails or at least the basics.
Currently I have a few Components for handling the actual "List". This being a ListContainer parent component (Holds the state for "Lists" which is an array of Lists) as well as all the Add/Update/Delete/Index(Set Lists initial state) functions.
Along with some child components (ListForm and List), both pretty self explanatory. List being a dumb component just holding the List title and description, and ListForm being the actual form to submit a new list.
I am using axios, and so far Create/Index/Update/Delete are working great for Lists. However I am running into the issue of being unsure how to handle the list items themselves. Currently in the rails side Lists has_many list_items and list_items belong_tolist`.
So rails has the relationship side all buttoned up...but im really unsure how to handle the actual items on the javascript side (for each list).
My first initial guess was to switch "List" dumb component to a smart component that handles state, in this state would be an array of "list_items" that belong to that particular list. When the List is loaded I imagine axios performing a GET request for that lists items within the component. And then basically handling add/delete/update similar to how the ListContainer component handles it for Lists (but instead making "List" component the de-facto container component for ListItems (which is currently a "dumb component")
Does this make sense? I honestly am still pretty new to react so handling relationships on the front end side is something im not familiar with yet. But storing state within a child that "belongs_to" a parent state/component makes the most sense initially? Unless I am overthinking it?
I think your proposed solution makes sense. Following your pattern you would probably have another Component, ListItem, and you would map through the listItems state in List and display a ListItem for each item.
That being said, many people learning react have a tendency to over-complicate it by having too many components/different files interacting. At one time the React community encouraged this, but they have since backed off on doing so. Many people now consider having presentational components (or "dumb" components, as you call them) an antipattern. See, for example, this note from Dan Abramov enter link description here
So I'm starting to learn Typescript+React in an industry setting (I've worked with a mixture of Coffee, Backbone, Marionettejs and a few others previously) and I was wondering what the idiomatic way to handle hierarchical views with respect to state transfer is.
For instance, let's pretend I'm attempting to make a card that will be placed on some dashboard. The card is composed of a header region, and a content region. The header region has a region for Title / subtitle, and a region for actions that can be done upon the card.
Now, lets say that there are a handful of objects from which the state necessary for rendering a card exists. What is the best way to pass down state to the children? For illustration lets say I have the following
class Report {
const title: string;
const subtitle: string;
const data: any[];
...
}
Go the Backbone way I know of passing a large options object that just has everything that an entire hierarchy needs and let each child grab what it wants and append what it wants. In this case, that would be effectively flattening the stateful objects and grabbing the queries to shove into props. For example <DashboardCard title={report.title} ... />
Pass the objects with state down so that the children can grab what they need. <DashboardCard report={ report }/>
Not quite flatten everything but pass down groups of attributes bundled into the props that the children expect. E.g <DashboardCard headerProps={ headerProps } ...>. This seems like it breaks down pretty fast because then a parent needs to know the exact shape of every child node props object. <DashboardCard headerProps={ } contentProps={ }/>
Scrap the hierarchy notion altogether, and create simple base components and then views need to manually stitch them altogether. This makes passing state trivial because it's literally view -> all components, but then there is no nice abstraction that can help with adding new features to components and other things.
Maybe (probably) there is something fundamental that I am missing with frontend development and react in general.
In the situation you are describing, I would lean towards passing down the necessary objects as props to the children (option 2). I would say that options 1 or 3 might be preferable if you have a large number of objects to pass down, and option 4 isn't too enticing due to the loss of abstraction as you mentioned.
Maybe at official flux website I saw a video were mentor said something like:
Only top-level React views should know about stores. All not top level
views should be dump and receive all information as properties.
Question: Is that right? Your argumentation, please
BUT, suppose you have some small React view Button.react that's reused on multiple pages. And suppose Button.react must know about some store data. If we won't fetch all data directly from the Button.react, we get a duplication of code at each top-level component which reuse Button.react. Is that ok for you?
I hope I am understanding your question.
One of the characteristics of React is its one-way data flow. Each component can be used by another component, just like one function can call another function. Just like a function, a React component should typically be able to get all the info it needs to do work (render itself) from the arguments passed into it. This is the function of props in React. When using Flux, sometimes the React Components, which are typically near the top of the view hierarchy, that actually fetch the data from the stores to pass down thru the application are called Controller-Views.
It is not an enforceable rule that every component doesn't become a Controller-View, getting its own state directly from a store, but it is a general practice for good reason. consider the two functions:
function renderToggleButton( isSelected ){
//... render the button
}
vs
function renderToggleButton(){
var isSelected = StateStore.getButtonSelectedState( id );
//... render the button
}
I think you will agree that the second function is more complicated and difficult to test. It has to know from where it is getting it's initial conditions. It also has to know how to identify itself in the context of the application. These are two things the function should not have to know.
Now imagine an application full of functions like this. If one function is misbehaving, it becomes very difficult to trace its inputs; to test it under controlled conditions. I hope that clarifies the guidance given for passing data thru the application as props.
I was wondering something about the reverse data flow in a React app.
Considering this question: data flow in react application and this article http://facebook.github.io/react/docs/thinking-in-react.html
We can see that the "normal" way to communicate from child to parent component is using callback passing through this.props.myCallback. (the StackOverflow article is a good example)
My questions are:
I understand the concept but what if we have deeper components? We would have to pass this callback to every component between the top parent component who hold the state and the actual triggering component. And if there is 3 to 4 components between them, I would have to pass the callback using this.props in each component.
Why not using an Event Emitter? I could send an event from the deep child component and then listen to this event in my top state parent component. This way we could avoid all the code between these two.
Please tell me what are your thoughts about this!
I learnt the hard way that it is a good practice to keep as much logic as possible in the highest level in the hierarchy of your components. Dan Abramov well expressed this idea in the article Smart and Dumb Components, where Smart Components are those that hold the logic, whereas Dumb Components are just meant to display.
So yes, the callback mechanism works well when you simply want to update the father's state, or you want the father to do an action whose logic you do not want to belong to the child.
When you have anything slightly more complicated than this I would suggest you go with a Flux-ish architecture of your taste. Flux indeed uses node's EventEmitter to communicate with the components.