Simulate inheritance using functional programming

2019-08-28 04:52发布

I have a component, I want to be able to override the render method and certain methods in that component. In React you cannot use inheritance. There could be a way using composition in functional programming, but how would you actually go about composing the render and all the methods into separate components. For React default component functions such as componentDidUpdate, would you be able to composition it as a separate component that you bring in to a higher-order component (HoC). How would props and state be passed through or accessed in each composed component of a HoC. It would be great to find an example of extending a component and overriding a method in some way.

1条回答
做个烂人
2楼-- · 2019-08-28 05:45

"Simulate inheritance using functional progr-" Stop, what? Why would you elect such a burden for yourself?

Functional programming isn't about translating the concepts you know in other paradigms. You'll need to learn many new things before you can begin to write meaningful programs.

Here's some stuff from Brian Lonsdorf's React Rally 2016 presentation – it might show you what the pot at the end of the rainbow can look like, but getting there is thing all on its own.

Let functional style be new and different; leave old habits at the door.

const { withReducer } = Recompose

const Reducer = g =>
({
  fold: g,
  contramap: f =>
    Reducer((state, action) => g(state, f(action))),
  map: f =>
    Reducer((state, action) => f(g(state, action))),
  concat: o =>
    Reducer((state, action) => o.fold(g(state, action), action))
})

const appReducer = Reducer((state, action) => {
  switch (action.type) {
    case 'set_visibility_filter':
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
})

const todoReducer = Reducer((state, action) => {
  switch (action.type) {
    case 'new_todo':
      const t = { id: 0, title: action.payload.title }
      return Object.assign({}, state, {
        todos: state.todos.concat(t)
      })
    default:
      return state
  }
})

const Component = g =>
({
  fold: g,
  contramap: f =>
    Component(x => g(f(x))),
  concat: other =>
    Component(x => <div>{g(x)} {other.fold(x)}</div>)
})


const classToFn = C =>
	(props) => <C {...props} />
  
const Hoc = g =>
({
  fold: g,
  concat: other =>
    Hoc(x => g(other.fold(x)))
})

// Example
// ======================

const todoApp = appReducer.concat(todoReducer)
                .contramap(action => Object.assign({filter: 'all'}, action))
                .map(s => Object.assign({}, s, {lastUpdated: Date()}))

const hoc = Hoc(withReducer('state', 'dispatch', todoApp.fold, {todos: []}))

const Todos = hoc.fold(({ state, dispatch }) =>
  <div>
    <span>Filter: {state.visibilityFilter}</span>
    <ul>
      { state.todos.map((t, i) => <li key={i}>{t.title}</li>) }
    </ul>
    <button onClick={() =>
      dispatch({ type: 'new_todo', payload: {title: 'New todo'}})}>
      Add Todo
    </button>
    <button onClick={() =>
      dispatch({ type: 'set_visibility_filter' })}>
      Set Visibility
    </button>
  </div>
)

const TodoComp = Component(classToFn(Todos))

const Header = Component(s => <h1>Now Viewing {s}</h1>)

const ProfileLink = Component(u => <a href={`/users/${u.id}`}>{u.name}</a>)


const App = Header.contramap(s => s.pageName)
						.concat(TodoComp)
						.concat(ProfileLink.contramap(s => s.current_user))
            .fold({ pageName: 'Home',
                    current_user: {id: 2, name: 'Boris' } })

ReactDOM.render(
  App,
  document.getElementById('container')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/recompose/0.26.0/Recompose.min.js"></script>
<div id="container">
    <!-- This element's contents will be replaced with your component. -->
</div>

查看更多
登录 后发表回答