In my experience, the cost of writing and also maintaining code is proportional to not necessarily the (typically fixed) cost of the boilerplate, but the "distance" between the various pieces needed for things to work.
Let's say I need a single bit of state in my component. If I store that in a local `useState(false)` and update it with `setState(newValue)`, it is extremely easy to write, and also follow what is happening and how it is happening.
But if dogmatically say that this is poor encapsulation - this state should live in some Redux store, with actions and action creators, reducer functions, etc. there is inherent complexity, and I think in the case of a single bit used in a single place that it is pretty easy to see that the complexity is disproportionate to the actual needs at hand.
But the way I see it is a sliding scale - your state starts to become more complex? or maybe how the state is updated requires some additional business logic? Maybe you want to start using that state in different places? Any of these reasons can be cause to introduce abstraction, but my preference is to introduce the smallest abstraction that achieves the benefit you're after.
Sometimes this abstraction is hoisting state up - now your component doesn't manage its own state, but rather it communicates with its parent via props+callbacks. Now its a little harder to follow (need to consider this component, its parent, and how the prop/callback are being used), but for that complexity we now get to share that state with sibling components.
Sometimes this abstraction is moving it to a reusable hook. Now you have a similar scenario where to understand how things are working you have to understand the component and the hook, but for that cost we can now share business logic.
Sometimes that abstraction is moving it into a redux store, with all the boilerplate that goes with it. For the right use case this is not a bad thing, but I would only want to do this if I'm getting a proportionate amount of value for the increased cost.
Let's say I need a single bit of state in my component. If I store that in a local `useState(false)` and update it with `setState(newValue)`, it is extremely easy to write, and also follow what is happening and how it is happening.
But if dogmatically say that this is poor encapsulation - this state should live in some Redux store, with actions and action creators, reducer functions, etc. there is inherent complexity, and I think in the case of a single bit used in a single place that it is pretty easy to see that the complexity is disproportionate to the actual needs at hand.
But the way I see it is a sliding scale - your state starts to become more complex? or maybe how the state is updated requires some additional business logic? Maybe you want to start using that state in different places? Any of these reasons can be cause to introduce abstraction, but my preference is to introduce the smallest abstraction that achieves the benefit you're after.
Sometimes this abstraction is hoisting state up - now your component doesn't manage its own state, but rather it communicates with its parent via props+callbacks. Now its a little harder to follow (need to consider this component, its parent, and how the prop/callback are being used), but for that complexity we now get to share that state with sibling components.
Sometimes this abstraction is moving it to a reusable hook. Now you have a similar scenario where to understand how things are working you have to understand the component and the hook, but for that cost we can now share business logic.
Sometimes that abstraction is moving it into a redux store, with all the boilerplate that goes with it. For the right use case this is not a bad thing, but I would only want to do this if I'm getting a proportionate amount of value for the increased cost.