evilsoft / crocks

A collection of well known Algebraic Data Types for your utter enjoyment.
https://crocks.dev
ISC License
1.59k stars 102 forks source link

Documentation for React Application #483

Closed stliang closed 4 years ago

stliang commented 4 years ago

Is your feature request related to a problem? Please describe. I am trying to use crocks for React App, to make a functional react component, I need to return JSX. If I want to return JSX based on an Either type, I can either detect if it is Right or Left, but if I need a value from Right, I am wrapped inside an Either type. Using bimap and put a return statement seems wrong. A use case for me is wrap an user object in Either. If the user is login I use Right(user) otherwise Left('anonymous'). Based on the user state, I want to return JSX or React Component in different way. Seems like I can not do that. I am started to think I can not use crocks with React.

I reviewed this example: https://github.com/dbagia/declarative-form-generator It maps some data to JSX, but does not involve ADT like Either or Maybe etc.

Describe the solution you'd like I would like to have in the documentation where crocks should be used. It looks like a backend thing and can be use at the front end in a limited way.

Describe alternatives for how you do this now N/A

Code

  const loginButton = () => {
    if (user) {
      return (
        <ListItem button onClick={logout}>
          <ListItemIcon>
            <Icon>lock</Icon>
          </ListItemIcon>
          <ListItemText
            primary={"Logout"}
            primaryTypographyProps={{ noWrap: true }}
          />
        </ListItem>
      )
    } else {
      return (
        <ListItem button onClick={login}>
          <ListItemIcon>
            <Icon>lock_open</Icon>
          </ListItemIcon>
          <ListItemText
            primary={"Login"}
            primaryTypographyProps={{ noWrap: true }}
          />
        </ListItem>
      )
    }
  }

Additional context Instead of if (user) {, I tried to use Either type and bimap. But I don't think I should use return statement within the bimap.

dalefrancis88 commented 4 years ago

This is a nice suggestion

If you'd like some advice for now i think you could look into doing it via pure function components, this is how i do it now. These are functions and work with the ADT's as normal functions do. You can then have whichever function creates the Either or Maybe and compose it with your functions to create components.

return compose(
  option(<OtherComponent />)
  map(createNormalComponent),
  createMaybeFromProps
)

Another option is using the IfElse helper to create the composed component by specifying the predicate and the two component functions

IfElse(IsLoaded, LoadingComponent, UserItem)

This feeds in to a more general set of requests to have a collection of tutorials and "Real World" scenarios

evilsoft commented 4 years ago

@stliang Where we work we use crocks and other functional/ADT libs in both the front end and backend. It really just depends on how you define the "edge" of your program. The edge being where all types are unwrapped and resolved to an answer.

For Sum types, like Either, we have a function that will let you fold out your result. It is called either and takes 2 functions. The first is called if the instance is in Left, the second if Right. either.

So you "could" use types like this for a simple if...then. That function you provided has a siggy of Props -> Component. So the real power is if you have function that work on the prop that can then be transformed before calling render. So you can change the props around conditionally before it hits the render. Like a mixin or a limited HOC that just transforms props. so you can have something like.

// isAdmin :: Props -> Either _ Props
// modifyAdmin :: Props -> Props
// modifyUser :: Props -> Props
// normalizeProps:: Props -> Props
// render Admin :: Props -> Component
// renderUser :: Props -> Component

// renderThat :: Props -> Component
const renderThat = compose(
  either(renderUser, renderAdmin),
  bimap(modifyUser, modifyAdmin),
  chain(isAdmin),
  compose(Either.of, normalizeProps) // lift into a Right
)

This is all consolidated into one composition for examples sake, but this really should be (2) or three compositions composed into one.

stliang commented 4 years ago

Thanks for the advice, I believe this should work. Will test it out.