fp-ts
fp-ts copied to clipboard
How would one use fp-ts with React?
📖 Documentation
From the docs:
IO actions are thunks so they are terminated by calling their () function application that executes the computation and returns the result. Ideally each application should call () only once for a root value of type Task or IO that represents the entire application.
How would one achieve something like that with a React application?
Right now, I'm calling ()
on Tasks and IOs multiple times, once per component, when I need to execute those Tasks/IOs.
Simple example, which i often do, and that I feel goes against what the docs advocate:
import { tryCatch } from 'fp-ts/lib/TaskEither';
const fetchJSON = tryCatch(/* fetch from remote */);
function MyComponent(props: Props) {
const [data, setData] = useState<MyType | undefined>();
useEffect(() => {
pipe(
fetchJSON,
chain(d => rightIO(setData(d))) // I suppose all state setting should be IOs?
)() // Calling () here, not at application root
}, []);
}
Note: I had a look at https://github.com/gcanti/elm-ts, but I don't want to use an Elm-like structure, just plain old React functions with hooks
I believe that in order to be able to run the monad only once at the top of your component tree, you would need to wrap all components somehow, otherwise this can't work. Examples of this are react-dream or MonadicReact (I have not tried them myself).
@amaurymartiny I would not recommend to use the hooks api at all, if you want to be as functional as possible. You would already need to wrap your useState
function in an IO
monad cause this function is not pure.
Essentially, what you have at the moment is a tiny bit of functional code inside an imperative React app. There is strictly speaking nothing wrong about that. It just means you are using fp-ts in a rather limited scope. You could proceed to create other such functional "bubbles" inside your app. This just means that your overall architecture will remain imperative.
Having only one Task or IO means you have managed to defined your application architecture in a functional manner. Your functional React app would then contain imperative bubbles. However, the imperative bubbles would never be executed in the functional context. Instead your code would simply combine the imperative bubbles and return them towards the application root.
The benefit of going 100% functional is that reasoning about the code becomes easier since the imperative bubbles that create unexpected consequences are executed "outside" your application. However, this also means less control over what the computer is doing since you are describing ideas rather than actions.
Ultimately you need to decide what parts of your app are functional or imperative. Functional parts will have more clarity while the imperative parts give you more precise control over execution.
However, this also means less control over what the computer is doing since you are describing ideas rather than actions.
This is exactly were we need to head towards! We just need to exchange "ideas" with "specifications" and need to trust that the machine or the used framework can find out the best way to do what we want. This should also be the default way cause "premature optimizations are the root of all evil".
Really good question.
Still not quite clear to me as an experienced react developer but a total newcomer to fp-ts. Would it be possible to re-open this and keep it open, until there are any examples or documentation / learning resources describing how to best use fp-ts in a react application? :-)
Did you try to wrap your hooks?
import * as IO from 'fp-ts/IO'
import { useEffect, useState } from 'react';
export const useEffectIO = <A>(
ma: IO.IO<A>,
deps: any,
) => useEffect(ma(), deps)
export const useCallbackIO = <R, A>(
ma: (r: R) => IO.IO<A>,
deps: any,
) => useCallback(r => ma(r)(), deps)
export const useStateIO = <A>(empty: A) => {
const [a, setA] = useState(empty)
return [a, (a) => () => setA(a)]
}
Disclaimer: Didn't try it on an IDE, so I hope it compiles.
Really good question.
Still not quite clear to me as an experienced react developer but a total newcomer to fp-ts. Would it be possible to re-open this and keep it open, until there are any examples or documentation / learning resources describing how to best use fp-ts in a react application? :-)
check elm-ts, it's creating elm
architecture with fp-ts and react and rxjs.
I think it's the purest form that you can get with fp-ts
Hmm if it takes this much plumbing, maybe it would be better just to switch to ReScript.
rescript is not pure FP, if you just want some functional you can use it like rescript here too.
I'm currently working on a project that includes some fairly complicated data manipulation on the client side, and I ended up writing this hook to be able to work with fp-ts in React in a way that feels a little 'cleaner' to me, w/ regard to mixing imperative and functional styles. Thought it might be of interest here; I welcome feedback/bug reports.
Just my two cents: I use fp-ts with React all the time, but I take a simpler hybrid approach. I wrote React as, well, react. My business logic exists in separate TS files which expose functions that can be called from my React components/hooks. My business logic is purely functional, but I use the React APIs as-is.
As much as I love FP, there's a point where the quest for purity imposes more costs than benefits. A hybrid approach when working with something like React is ideal IMO.
Just my two cents: I use fp-ts with React all the time, but I take a simpler hybrid approach. I wrote React as, well, react. My business logic exists in separate TS files which expose functions that can be called from my React components/hooks. My business logic is purely functional, but I use the React APIs as-is.
As much as I love FP, there's a point where the quest for purity imposes more costs than benefits. A hybrid approach when working with something like React is ideal IMO.
If you use GraphQL and a fairly sophisticated GraphQL client such as Relay ideally there should not be much business logic left in your own client code (except a little local UI state). Most if not all would be in your GraphQL server and GraphQL client. But I get your point - you're basically saying we should not waste time wrapping non-functional api's and just use it within the functions in those cases right. Same as you'd probably do in an Express server. The point should be to simplify, not to fight hard to complete by wrapping non-functional api's.
I stumbled upon the following article: https://andywhite.xyz/posts/2021-01-28-rte-react/ It does show towards the end, how to use fp-ts with React, it could be a starting point?
NB: Beware, it's main focus is to showcase ReaderTaskEither.