Skip to content

Hooks?#1

Closed
maddie927 wants to merge 12 commits into
masterfrom
hooks
Closed

Hooks?#1
maddie927 wants to merge 12 commits into
masterfrom
hooks

Conversation

@maddie927

@maddie927 maddie927 commented Nov 1, 2018

Copy link
Copy Markdown
Owner

[todo: a better description]

Examples: Counter, ControlledInput, others

In addition to kind of liking this api for these basic cases, I'm realizing it enables many more advanced features (action/update pattern as well as more complicated features that haven't existed before like refs and context) without complicating the basic ones.

@maddie927 maddie927 force-pushed the hooks branch 3 times, most recently from e153c01 to c10fa4e Compare November 1, 2018 06:39
@born2defy

Copy link
Copy Markdown

Hey there - I took a look at adding the React.useContext hook to what you have already done here. In order to maintain some type safety, I wanted to add a phantom type to Render and Component to track the type of the Context used. The idea would be for the compiler to enforce that we provide the required context prior to rendering out to the DOM (and assuming one distinct context is used for an entire app). In my mind, it should work much like the Reader monad. I was able to rewrite element to only act on components whose context type can be coerced to Void (no context defined), so we don't really muddy up non-context components other than the extra type parameter, but the problem is tracking the context when nesting components.

The issue is that when you add a context-dependent component as a child of another component, we first have to render it to JSX - discarding the type info that we were tracking. What we really want instead is to unify the context type of the child with that of the parent before we discard that type info and turn the child into JSX. Have you already given thought to this situation?

@maddie927

Copy link
Copy Markdown
Owner Author

Not much more than you already have. Also at this point this is just an experiment, and the bigger lingering issue is how unsafe it is. I don't think I'll put much effort into it if I can't figure that detail out.

@born2defy

Copy link
Copy Markdown

Aside from the obvious danger in Context, you feel that the other hook abstractions (useState, useEffect) are more unsafe than the previous formulation?

@maddie927

Copy link
Copy Markdown
Owner Author

Yes, because dynamically reordering or omitting Render effects breaks the types. It probably won't work without some sort of indexing system.

@maddie927

Copy link
Copy Markdown
Owner Author

I would like to get it to work though :)
And if it does work, useContext and useRef (plus forwarding) are high on my list too

@born2defy

Copy link
Copy Markdown

Yes, I agree. Especially as this seems to be the direction React is headed in. But very tricky to type it safely without introducing a whole lot of machinery.

@maddie927

Copy link
Copy Markdown
Owner Author

I think IxMonad has made this type-safe (or if it currently has bugs they at least should be fixable). Qualified do makes using it more palatable, so hopefully that gets merged 🙂

@maddie927

Copy link
Copy Markdown
Owner Author

I also added a few more examples as I've added hooks. The useRef example shows off the modularity of hooks pretty well.

@born2defy

Copy link
Copy Markdown

This is looking pretty promising - especially with qualified do. I wonder what your thoughts are on useContext though. It seems you have to track the context type of the children within the parent components and I don’t see how that is possible without refacoring the component api to make adding context-typed children explicit.

@maddie927

Copy link
Copy Markdown
Owner Author

Yeah... I started that and commented it back out. Alternatively it could just use the Context’s type but force it to be a Maybe when reading.

@born2defy

Copy link
Copy Markdown

The issue with Context providing a Maybe value to its children is that is basically destroys the entire semantics of using Context in the first place. I always thought of it just like a Reader monad - where we assume some context value is always provided at the top level and then just let it default to Void when none of the children require it. But then you have to track it from the children up which requires refactoring.

@maddie927

Copy link
Copy Markdown
Owner Author

The trouble is that it isn't one context -- it's associated with a specific Context object used for retrieval. I'm not sure how to represent that in the component types.

@maddie927

Copy link
Copy Markdown
Owner Author

It also sort of makes sense to build a component which can use context if it's provided, but doesn't need to.

@born2defy

Copy link
Copy Markdown

The issue with a maybe Context is that it prevents you from writing components that truly depend on a context being provided which I think is one of the major use cases. React allows multiple contexts but I think that is impossible to model, which is why in the experiments I had done I always ended up constraining the component type to require a single context object at the top level that all of the children nodes could be coerced to. The way I had constructed it prior to hooks was to provide the context to the children via props (under the hood, similar to an HOC), and then I used the props type to track it back up the chain. Even then though type inference got pretty hairy.

@maddie927

Copy link
Copy Markdown
Owner Author

Hmm.. only allowing a single context object seems like a bigger trade off than a Maybe, but I'll keep thinking about it.

The Maybe case doesn't seem terrible for both required and optional contexts:

x <- useContext requiredContext
useEffect [toKey x] do ...console warn/error if Nothing
React.pure $ case x of
  Nothing -> ...warn or leave empty
  Just x' -> ...normal render
x <- fromMaybe defaultContextValues <$> useContext optionalContext
React.pure $ ...normal render

@born2defy

Copy link
Copy Markdown

You may be right. I never really thought about sacrificing multiple contexts because it seemed so natural to unify them at the top level anyway into a single atom. I thought trying to get the compiler to enforce that I provided the correct context seemed a lot nicer than sniffing out runtime errors.

@maddie927

Copy link
Copy Markdown
Owner Author

@maddie927 maddie927 closed this Jan 27, 2019
@maddie927 maddie927 deleted the hooks branch January 27, 2019 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants