{"id":17228570,"url":"https://github.com/benadamstyles/pure-render-prop-component","last_synced_at":"2025-07-10T09:39:10.091Z","repository":{"id":57332089,"uuid":"121765308","full_name":"benadamstyles/pure-render-prop-component","owner":"benadamstyles","description":"Use the render prop pattern without sacrificing component purity","archived":false,"fork":false,"pushed_at":"2019-05-01T16:04:21.000Z","size":2180,"stargazers_count":6,"open_issues_count":13,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-28T19:23:05.830Z","etag":null,"topics":["react","react-component","react-native","reactjs","render-callback","render-prop","render-props"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benadamstyles.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-02-16T15:18:15.000Z","updated_at":"2019-10-25T06:07:19.000Z","dependencies_parsed_at":"2022-09-21T03:51:53.137Z","dependency_job_id":null,"html_url":"https://github.com/benadamstyles/pure-render-prop-component","commit_stats":null,"previous_names":["leeds-ebooks/pure-render-prop-component","leeds-ebooks/pure-render-callback-component"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/benadamstyles/pure-render-prop-component","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benadamstyles%2Fpure-render-prop-component","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benadamstyles%2Fpure-render-prop-component/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benadamstyles%2Fpure-render-prop-component/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benadamstyles%2Fpure-render-prop-component/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benadamstyles","download_url":"https://codeload.github.com/benadamstyles/pure-render-prop-component/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benadamstyles%2Fpure-render-prop-component/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263593460,"owners_count":23485853,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["react","react-component","react-native","reactjs","render-callback","render-prop","render-props"],"created_at":"2024-10-15T04:44:30.868Z","updated_at":"2025-07-10T09:39:10.065Z","avatar_url":"https://github.com/benadamstyles.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PureRenderPropComponent\n\n[![npm version](https://badge.fury.io/js/pure-render-prop-component.svg)](https://www.npmjs.com/package/pure-render-prop-component)\n[![Build Status](https://travis-ci.org/Leeds-eBooks/pure-render-prop-component.svg?branch=master)](https://travis-ci.org/Leeds-eBooks/pure-render-prop-component)\n[![Greenkeeper badge](https://badges.greenkeeper.io/Leeds-eBooks/pure-render-prop-component.svg)](https://greenkeeper.io/)\n\n```sh\nnpm install --save pure-render-prop-component\n# or\nyarn add pure-render-prop-component\n```\n\nThe [render prop pattern](https://reactpatterns.com/#render-callback) in React allows us to build highly functional components, but it doesn’t play well with how React currently manages re-rendering.\n\n```js\nimport React, {Component} from 'react'\n\nclass CurrentTime extends Component {\n  state = {currentTime: Date.now()}\n  render() {\n    return this.props.children(this.state.currentTime)\n  }\n}\n\nclass App extends Component {\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003cCurrentTime\u003e\n          {// *\n          currentTime =\u003e (\n            \u003cdiv\u003e\n              \u003cp\u003e{this.props.pageTitle}\u003c/p\u003e\n              \u003cp\u003e{currentTime}\u003c/p\u003e\n            \u003c/div\u003e\n          )}\n        \u003c/CurrentTime\u003e\n      \u003c/div\u003e\n    )\n  }\n}\n```\n\nHere, our `CurrentTime` component will re-render every time our `App` component renders, even if neither `CurrentTime`’s `state` nor its `props` (in this case, its `children` function) have changed.\n\nHowever, changing `CurrentTime` to [inherit from `PureComponent`](https://reactjs.org/docs/react-api.html#reactpurecomponent) doesn’t help. [The React docs explain why](https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent): `PureComponent` compares `state` and `props`, and **only** if a property of `state` or of `props` has changed, does it re-render. In the above case, every time `App` re-renders, the render prop supplied to `CurrentTime` (marked `*`) is recreated. Two functions which look the same are still two different functions, so `CurrentTime#props.children` has changed, and `CurrentTime` re-renders.\n\nWe can solve this by, [as the React docs put it](https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent), defining the function “as an instance method”, in other words, moving the function out of our `App` component’s `render` method.\n\n```js\nimport React, {Component, PureComponent} from 'react'\n\nclass CurrentTime extends PureComponent {\n  state = {currentTime: Date.now()}\n  render() {\n    return this.props.children(this.state.currentTime)\n  }\n}\n\nclass App extends Component {\n  currentTimeCallback = currentTime =\u003e (\n    \u003cdiv\u003e\n      \u003cp\u003e{this.props.pageTitle}\u003c/p\u003e\n      \u003cp\u003e{currentTime}\u003c/p\u003e\n    \u003c/div\u003e\n  )\n\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003cCurrentTime\u003e{this.currentTimeCallback}\u003c/CurrentTime\u003e\n      \u003c/div\u003e\n    )\n  }\n}\n```\n\nNow, `currentTimeCallback` is only created once. `PureComponent` compares `props` before and after the re-render of `App`, finds that the `children` function hasn’t changed, and aborts the re-render of `CurrentTime`. Performance improved!\n\n**But there is a big problem waiting to happen.** Our `currentTimeCallback` doesn’t just depend on the `currentTime` argument passed down from our `CurrentTime` component. It also renders `App`’s `props.pageTitle`. But with the above setup, when `pageTitle` changes, `currentTimeCallback` will not re-render. It will show **the old `pageTitle`**.\n\nI struggled with this problem, trying all sorts of horrible hacks, until I came across [this Github issue on the React repo](https://github.com/facebook/react/issues/4136), and the [suggestion](https://github.com/facebook/react/issues/4136#issuecomment-112168425) by a React developer of a possible solution. `PureRenderPropComponent` is my implementation of that solution.\n\n## Usage\n\n```js\nimport React, {Component} from 'react'\nimport PureRenderPropComponent from 'pure-render-prop-component'\n\nclass CurrentTime extends PureRenderPropComponent {\n  state = {currentTime: Date.now()}\n  render() {\n    return this.props.children(this.state.currentTime, this.props.extraProps)\n    // NOTE: PureRenderPropComponent also supports a prop named 'render' ☟\n    return this.props.render(this.state.currentTime, this.props.extraProps)\n  }\n}\n\nclass App extends Component {\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003cCurrentTime extraProps={{pageTitle: this.props.pageTitle}}\u003e\n          {(currentTime, extraProps) =\u003e (\n            \u003cdiv\u003e\n              \u003cp\u003e{extraProps.pageTitle}\u003c/p\u003e\n              \u003cp\u003e{currentTime}\u003c/p\u003e\n            \u003c/div\u003e\n          )}\n        \u003c/CurrentTime\u003e\n        {\n          // NOTE: PureRenderPropComponent also supports a prop named 'render'\n          // (instead of 'children') ☟\n        }\n        \u003cCurrentTime\n          extraProps={{pageTitle: this.props.pageTitle}}\n          render={(currentTime, extraProps) =\u003e (\n            \u003cdiv\u003e\n              \u003cp\u003e{extraProps.pageTitle}\u003c/p\u003e\n              \u003cp\u003e{currentTime}\u003c/p\u003e\n            \u003c/div\u003e\n          )}\n        /\u003e\n      \u003c/div\u003e\n    )\n  }\n}\n```\n\nNow, our render prop will always re-render when, and **only** when, `CurrentTime#state.currentTime` or `App#props.pageTitle` change.\n\nYou can also pass other props into your render prop component and they will be treated in the same way.\n\n```js\nimport React, {Component} from 'react'\nimport PureRenderPropComponent from 'pure-render-prop-component'\n\nclass CurrentTime extends PureRenderPropComponent {\n  state = {currentTime: Date.now()}\n\n  format(timestamp) {\n    return String(new Date(timestamp))\n  }\n\n  render() {\n    const time = this.props.format\n      ? this.format(this.state.currentTime)\n      : this.state.currentTime\n    return this.props.children(time, this.props.extraProps)\n  }\n}\n\nclass App extends Component {\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003cCurrentTime\n          format={true}\n          extraProps={{pageTitle: this.props.pageTitle}}\u003e\n          {(currentTime, extraProps) =\u003e (\n            \u003cdiv\u003e\n              \u003cp\u003e{extraProps.pageTitle}\u003c/p\u003e\n              \u003cp\u003e{currentTime}\u003c/p\u003e\n            \u003c/div\u003e\n          )}\n        \u003c/CurrentTime\u003e\n      \u003c/div\u003e\n    )\n  }\n}\n```\n\nHere, our render prop will also re-render when the boolean passed into `CurrentTime`’s `format` prop changes.\n\n## Caveats \u0026 Assumptions\n\n* `PureRenderPropComponent` assumes you will either use a `props.children` prop:\n\n  ```js\n  \u003cRenderCallbackComponent\u003e\n    {(val, extraProps) =\u003e \u003cNode /\u003e}\n  \u003cRenderCallbackComponent\u003e\n  ```\n\n  or a “render prop”:\n\n  ```js\n  \u003cRenderCallbackComponent render={(val, extraProps) =\u003e \u003cNode /\u003e} /\u003e\n  ```\n\n  Using either one for a purpose other than [the render prop pattern](https://reactpatterns.com/#render-prop) will lead to unexpected behaviour, including but not limited to a stale UI due to missed renders.\n\n## How does it work?\n\n```js\nshouldComponentUpdate(nextProps, nextState) {\n  const {props, state} = this\n  const omitKeys = ['extraProps', 'children', 'render']\n  return (\n    !shallowEqual(state, nextState) ||\n    !shallowEqual(omit(props, omitKeys), omit(nextProps, omitKeys)) ||\n    !shallowEqual(props.extraProps, nextProps.extraProps)\n  )\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenadamstyles%2Fpure-render-prop-component","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenadamstyles%2Fpure-render-prop-component","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenadamstyles%2Fpure-render-prop-component/lists"}