3 ms·
At the end of the day, components will always need a way to interface with state. Often times you want local component state because the cost of abstracting tha
by joshfee 5y ago
At the end of the day, components will always need a way to interface with state. Often times you want local component state because the cost of abstracting that tiny bit of state into some top level application state is disproportionately high - it just isn't a pragmatic choice. And for application state, ultimately if you have any interactivity in your application (which, if you have state, presumably you do), at _some point_ you're going to have to do something other than "map props to HTML".
> If you're doing anything other than that, your function components aren't really pure functions.
Nobody claims they are pure functions. They aren't - but that doesn't really matter. Its an API choice that encourages productivity while still having tools for the abstraction that you are advocating for.
State hooks can be seen as a sibling of props - both are inputs to the component, but whereas props "push" data into the component, hooks "pull" data from somewhere else. This _is_ separating concerns - when a component needs data that isn't inherently something the calling parent owns, then making it a prop would just be forcing that component's concerns up the tree. Often it is better for it to just be an impl detail.
I don't find the article here super compelling, and as a general practice it is often better to author your own custom hooks that encapsulate things like the underlying HTTP request away so that your component is only dealing with its own concerns, but functionally there's no difference and its just a matter of how you split up your code.
- octet1 5y ago> the cost of abstracting that tiny bit of state into some top level application state is disproportionately high - it just isn't a pragmatic choice What other costs are there besides boilerplate?
- joshfee 5y agoIn 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.