Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NGRX and state management for child components

Currently refactoring Angular application to use ngrx store and have two options. Here's our basic structure of the application. I believe that most Angular applications are built the same way:

AppComponent
|-- ContainerComponent
    |-- ChildComponent1
    |   |-- GrandChildComponent
    |       |-- GrandGrandChildComponent
    |-- ChildComponent2

ContainerComponent has injected Store<AppState>. The problem I'm trying to solve is that GrandGrandChildComponent (say, DropDownMenu component) has to change its behaviour based on the state of the application (i.e. disable some menu items on certain conditions that happen in the store) and emit an event when clicked on menu item so ContainerComponent (or any other component, not necessary ancestor) could react upon it.

There are a couple of ways of solving it:

  1. Communicate between components using @Input/@Output
  2. Inject Store into any component that requires knowing the state

Option 1 is what most common/recommended pattern that I've read in the docs. So only ContainerComponent is fat and all children are thin/dumb and relies on the state that comes in through @Input.

But from what I see this approach adds a lot of boilerplate where you have to add unnecessary attributes just to pass-through the state to GrandChild components. And also it breaks the separation of concerns principle because any intermediate components have to know what is required in the components that are below. And it's not easy to make generic component if it knows of the details that are available only on GrandComponents.

On the other hand, approach 2 seems to solve the issue of separating concerns and it also seems to be cleaner implementation. But as I'm relatively new in using redux pattern I'm not sure if that's the way to go and perhaps I don't know any pitfalls that I might face when I'll be too deep in refactoring.

IMO, both approaches provide an easy way of testing of each component which is a huge for me.

I'm in doubt which approach to take. What pitfalls should I be aware of?

Thanks

like image 713
sickelap Avatar asked Jul 14 '17 08:07

sickelap


2 Answers

Here's what Dan Abramov creator of redux (ngrx is inspired from redux so a lot of the same ideas apply to ngrx) has to say on the topic:

When to Introduce Containers? I suggest you to start building your app with just presentational components first. Eventually you’ll realize that you are passing too many props down the intermediate components. When you notice that some components don’t use the props they receive but merely forward them down and you have to rewire all those intermediate components any time the children need more data, it’s a good time to introduce some container components. This way you can get the data and the behavior props to the leaf components without burdening the unrelated components in the middle of the tree. This is an ongoing process of refactoring so don’t try to get it right the first time. As you experiment with this pattern, you will develop an intuitive sense for when it’s time to extract some containers, just like you know when it’s time to extract a function.

Source: https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#7dc5

like image 141
seescode Avatar answered Oct 06 '22 23:10

seescode


This is much depends on your use-case for each Component whether how many @Output and @Input they have.

I have been using your 1st-approach for quite a time and it seems to be fine if your Child / lower-order component does very simple interaction ( take data top-down , then emit an Event back to Parent ) or a Dumb Component ( only takes Input ) . Sure that my Container was very big

But if one of your Child components ended up having many I/O and having many other child-child components which also having the same, you might want to treat them as a Big-Child ( @Output() from Child will stay in Big-Child component instead of passed to GrandParent component )

Having too much @Output() from bottom-up might give you headache as well :) . 2nd-approach would make your Component easier to read.

AppComponent |-- ContainerComponent |-- ChildComponent1 | |-- GrandChildComponent | |-- GrandGrandChildComponent |-- ChildComponentWithManyIO => Make them to be self-managed |--GrandChild with only Input |--GrandChild with many Inputs/Outputs ( Self-managed )

My idea of managing states came up from here : https://www.youtube.com/watch?v=eBLTz8QRg4Q

like image 41
Vi Elite Avatar answered Oct 06 '22 23:10

Vi Elite