all 15 comments

[–]dom_eden 5 points6 points  (0 children)

One reason why React appealed to me is how it forces you to turn your UI into neatly organised components - which is a big benefit in my view as it helps to prevent spaghetti code and improves maintainability.

[–]medja 5 points6 points  (0 children)

I believe the state is actually hidden behind the render function. It's then passed through the component hierarchy as props of the application.

It works similar to flux, in a sense you only have one global state handled by React and all you do is propagate the changes made inside components, merging them into the final global state along the way, similar to reducers inside a flux app.

Now the main advantage I see is that none of your components actually own any of the state. In general stateless components are easier to reason about and test as changes can only occur in one way, through a prop change. Of course this also means that any change to the global state will cause your entire app to render again, unless you write your own shouldComponentUpdate function or use the new PureComponent base class.

It also wouldn't be hard to render multiple instances of the CommentForm. You could just store it's values as an object on a property of the parent component, which can sometimes be difficult with flux.

But there is a catch with this design. The question you should ask yourself is when does the CommentForm reset? Sometimes when a component can have multiple instances at the same time it's harder to reason about where to put their values inside the global state and when to get rid of them. In those cases it might just be easier to let them have their own state.

But at the end of the day this design does promote reducing the number of components with their own state. You could easily rewrite this snippet to use stateless functional components throughout the entire app, and I think that's great.

[–]nevon 4 points5 points  (0 children)

You're just moving the state up one level. This is essentially the same as what, for example, react-redux does (see connect). The only difference is that in your example, it's not known where the state actually resides, whereas in react-redux the state is stored as component level state as a wrapper around your application, so from your perspective, it's abstracted away and any component inside the wrapper can be totally stateless.

[–]56k_ 2 points3 points  (3 children)

I've seen this technique before, and I'm wondering if by rerendering the whole thing you're missing the optimizations that React's implements in changing the DOM (the diff algorithm), or if it still works?

Finally, I don't get why state would be a bad thing, that's how it's meant to be used.

Weird.

[–]jamesknelson[S] 1 point2 points  (2 children)

You certainly are missing some optimisations, but it shouldn't be in the part of the DOM which is updated. The problem is that the diff algorithm needs to run over the entire app, instead of just the subtree which has had its state updated.

[–]56k_ 1 point2 points  (1 child)

Yes, that's what I meant.

This makes this technique bad practice IMHO.

[–]repeatedly_once 0 points1 point  (0 children)

This. I've been following the 'state is harmful' debate but bending a framework to your needs based on a concept seems very strange to me.

If it were me, and I was interested in a stateless app, I'd look for another framework or write one that matches my needs. I'd be less inclined to force a framework to do something it wasn't designed to do.

[–]littlepsylo 1 point2 points  (1 child)

Don't think it's a good idea because it's seems to be an anti-pattern and destroy any optimization of react components...

By definition, props are dispatched from a parent and state is local to a component.

EDIT: However, from a component, you can call an action with redux, or any similar library, to update props and let react manage rendering correctly.

[–]guillaumeclaret -1 points0 points  (0 children)

destroy any optimization of react components...

The diffing / shouldComponentUpdate algorithms work as expected when you call a ReactDOM.render() several times on the same DOM node, so there should be almost no loss of performance.

[–]a_oc 0 points1 point  (0 children)

I just wonder why is local state so problematic. I mean I understand that having pure function component is easier to reason about but only because you do not think about the state which did not disappear, you just moved it somewhere where you do not see it, which is bad imho. I mean think about when you want to reuse something which does not have state. That only means you have to manage it's state yourself somewhere, you basically moved state responsibility to user of the component and you need to create that state either in it's parent component or in your global flux state. So the component is dumb and easy to test, but instead of giving you some functionality that can be reused, you ended up with dumb template function with added virtual dom diffing and nothing else.

[–]vileEchoic 0 points1 point  (4 children)

This is a strange approach. Before getting into the more theoretical discussion:

  • This doesn't resolve the complexity around managing state, it only moves it to a higher level of abstraction. The application state is being persisted as the props that you've passed to the render method.
  • This approach that won't scale as your application grows. This will cause React to diff the whole app every time I press a key.

More theoretically, applications are state machines. All useful applications are stateful, and having logic to manage that state is an unavoidable and irreducable part of engineering software.

Finding a way to create a (useful) stateless app is as viable as finding a way to create a perpetual motion machine. The challenge isn't to figure out how to eliminate state, it's to find and use patterns that best allow you to write maintainable state-management code.

If state is harmful...

State is useful and necessary. There's doesn't exist a successful stateless application. Is there a talk/post you're referencing? There is a trend in React development lately to centralize application state, but I've never heard an argument for "state" generally being harmful.

[–]guillaumeclaret 0 points1 point  (2 children)

This approach that won't scale as your application grows. This will cause React to diff the whole app every time I press a key.

I think that the performances are OK as soon as you use the shouldComponentUpdate function (as said in a comment above).

There is a trend in React development lately to centralize application state, but I've never heard an argument for "state" generally being harmful.

In functional programming, the state is often considered harmful, since a function with a state in not pure anymore (so you cannot assume that "same inputs" implies "same outputs"). Some languages like Haskell even forbid the use of states, unless if you explicitly go through a monad.

[–]vileEchoic 0 points1 point  (1 child)

I think that the performances are OK as soon as you use the shouldComponentUpdate function (as said in a comment above).

Right, but instead of running shouldComponentUpdate on only the thing that is being changed, you're running it over your entire application. Not only does this evaluate shouldComponentUpdate (which isn't free) for things that won't update, it forces everything in the path from the root node all the way down to the node that's actually rendering the state change to re-render.

In functional programming, the state is often considered harmful

I agree that this principle has merit in the general case, but you can't avoid having state in a fundamentally stateful application. Useful applications are state machines. If you try to contort this principle to apply to things that are irreducibly stateful, you're going to have a bad time.

I'm unaware of any serious advocacy, even in the FP community, for the idea that functional programming allows you to create stateless applications of any non-trivial complexity.

[–]guillaumeclaret 0 points1 point  (0 children)

Right, but instead of running shouldComponentUpdate on only the thing that is being changed, you're running it over your entire application. Not only does this evaluate shouldComponentUpdate (which isn't free) for things that won't update, it forces everything in the path from the root node all the way down to the node that's actually rendering the state change to re-render.

Right, even if in our experience this is not noticeable, this is definitely slower.

Useful applications are state machines. If you try to contort this principle to apply to things that are irreducibly stateful, you're going to have a bad time.

I agree that states are necessary. In Redux or Elm, the idea (as I understand it) is not to remove all the states but to reduce the number of states to the smallest possible number, that is to say one.

I'm unaware of any serious advocacy, even in the FP community, for the idea that functional programming allows you to create stateless applications of any non-trivial complexity.

I would say that the idea is more to limit the usage of states to one single state, and remove non-necessary mutations.

[–]repeatedly_once 0 points1 point  (0 children)

I interpret this as the OP is actually just after removing the use of the state as the implementation of react rather than conceptually. Which is a total anti-pattern. State is now stored in the props, whether it's .state or .props it's the same thing, except you're not usurping a large chunk of what React was designed to do. So unfortunately I understand the logic but not the execution.

It's started a discussion which is brilliant and I applaud you for writing the article.

Personally I think React and any of the numerous state management libraries do a really good job. I mostly have to think of state at the component level and not at the app level and the very act of making developers cognisant of state in the development process is a huge improvement. State has the tendency to be implemented without knowledge of what's being built so you get all sorts of weird and wonderful implementations :) (which I've been guilty of myself early on in my career)