{"id":13481429,"url":"https://github.com/collardeau/react-with-state-props","last_synced_at":"2025-04-14T23:15:22.778Z","repository":{"id":57344459,"uuid":"123945250","full_name":"collardeau/react-with-state-props","owner":"collardeau","description":"A container render-prop component to initialize, handle and derive state in React.","archived":false,"fork":false,"pushed_at":"2018-03-30T10:26:44.000Z","size":730,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-14T23:15:08.625Z","etag":null,"topics":["react","react-component","state-props","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/collardeau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-05T16:14:39.000Z","updated_at":"2018-07-31T02:08:16.000Z","dependencies_parsed_at":"2022-09-11T09:01:03.174Z","dependency_job_id":null,"html_url":"https://github.com/collardeau/react-with-state-props","commit_stats":null,"previous_names":["collardeau/react-senna"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/collardeau%2Freact-with-state-props","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/collardeau%2Freact-with-state-props/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/collardeau%2Freact-with-state-props/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/collardeau%2Freact-with-state-props/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/collardeau","download_url":"https://codeload.github.com/collardeau/react-with-state-props/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248975329,"owners_count":21192210,"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","state-props","typescript"],"created_at":"2024-07-31T17:00:51.761Z","updated_at":"2025-04-14T23:15:22.759Z","avatar_url":"https://github.com/collardeau.png","language":"TypeScript","funding_links":[],"categories":["Components"],"sub_categories":["Data"],"readme":"[![Build Status](https://travis-ci.org/collardeau/react-with-state-props.svg?branch=master)](https://travis-ci.org/collardeau/react-with-state-props)\n[![Coverage Status](https://coveralls.io/repos/github/collardeau/react-with-state-props/badge.svg?branch=master)](https://coveralls.io/github/collardeau/react-with-state-props?branch=master)\n[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)\n\n# react-with-state-props\n\nA container render-prop component to initialize, handle and derive state in React.\n\n## Installation\n\n`npm install react-with-state-props --save`\n\n## Examples\n\nCreate some state:\n\n```javascript\nimport Container from \"react-with-state-props\"\n\n// ...\n\n\u003cContainer\n  state={{ counter: 0 }}\n  render={props =\u003e {\n    // props ready-to-go based on the state you provided:\n    console.log(props);\n    // { counter: 0, setCounter: [Function] }\n    // props.setCounter is automatically generated\n    return \u003cMyApp {...props} /\u003e; // whatever JSX/Comp you want\n  }}\n/\u003e;\n```\n\nYou can derive state (and keep your original state as simple as possible):\n\n```javascript\n\u003cContainer\n  state={{ counter: 0 }} // original state\n  deriveState={[\n    // derive `isOdd` when `counter` changes\n    {\n      onStateChange: [\"counter\"],\n      derive: state =\u003e ({\n        isOdd: Boolean(state.counter % 2)\n      })\n    }\n  ]}\n  render={props =\u003e {\n    // { counter: 0, setCounter: [Function], isOdd: false }\n    return \u003cCounter {...props} /\u003e; // your JSX\n  }}\n/\u003e;\n\n// You can derive state from derived state:\n\n\u003cContainer\n  state={{ counter: 1 }}\n  deriveState={[\n    {\n      onStateChange: [\"counter\"],\n      derive: ({ counter }) =\u003e ({\n        isOdd: Boolean(counter % 2)\n      })\n    },\n    {\n      onStateChange: [\"isOdd\"], // now react to `isOdd` changes\n      derive: ({ isOdd }) =\u003e ({\n        isEven: !isOdd\n      })\n    }\n  ]}\n  render={props =\u003e {\n    // { counter: 0, setCounter: [Function], isOdd: true, isEven: false }\n    return \u003cCounter {...props} /\u003e; // your JSX\n  }}\n/\u003e;\n\n```\n\nYou can also create custom state handlers:\n\n```javascript\n\n\u003cContainer\n  state={{ counter: 0 }}\n  withHandlers={{\n    add1: props =\u003e () =\u003e {\n      // props.setCounter was created in Container\n      props.setCounter(props.counter + 1)\n    }\n  }}\n  render={props =\u003e {\n    console.log(props);\n    // { counter: 0, add1: [Function], setCounter: [Function] }\n    return \u003cCounter {...props} /\u003e; // your JSX\n  }}\n/\u003e;\n\n// another example with multiple handlers and some syntax shorthand\n// where we want to render a counter that we can increment by 1 or 10, or reset:\n\n\u003cContainer\n  state={{ counter: 0 }}\n  withHandlers={{\n    incr: ({ counter, setCounter }) =\u003e num =\u003e setCounter(counter + num),\n    incrBy1: ({ incr }) =\u003e () =\u003e incr(1), // using custom handler just defined\n    incrBy10: ({ incr }) =\u003e () =\u003e incr(10),\n    reset: ({ setCounter }) =\u003e () =\u003e setCounter(0)\n  }}\n  omitProps={[\"setCounter\", \"incr\"]} // drop props before the render function\n  render={props =\u003e {\n    console.log(props);\n    // { counter: 0, incrBy1: [Function], incrBy10: [Function], reset: [Function] }\n    return \u003cCounter {...props} /\u003e; // your JSX\n  }}\n/\u003e;\n\n```\n\n**Putting it all together**, here is a basic Todo App, with the ability to create todos and toggle them between done/undone.\nWe keep the todos in an object for easier lookup (by key), and derive an array of todos on changes that we render:\n\n```javascript\n\nimport React from \"react\";\nimport Container from \"react-with-state-props\";\n\n// define state, derived state and state handlers\n\nconst state = {\n  todos: {},\n  newInput: \"\"\n};\n\nconst deriveState = [\n  {\n    onStateChange: \"todos\", // when `state.todos` change\n    derive: ({ todos }) =\u003e ({\n      // derive `todosByDate` array\n      todosByDate: Object.keys(todos)\n        .map(key =\u003e todos[key])\n        .sort((a, b) =\u003e b.stamp - a.stamp)\n    })\n  }\n];\n\nconst withHandlers = {\n  changeInput: ({ setNewInput }) =\u003e e =\u003e {\n    // controlled text input\n    setNewInput(e.target.value); // setNewInput is created from `newInput` state\n  },\n  mergeTodos: ({ setTodos, todos }) =\u003e newTodos =\u003e {\n    // other handlers will use this\n    setTodos({ ...todos, ...newTodos }); // setTodos is created from `todos` state\n  },\n  submit: ({ mergeTodos, setNewInput, newInput }) =\u003e () =\u003e {\n    // submit new todo\n    if (!newInput) return;\n    const title = newInput.trim();\n    mergeTodos(createTodo(title));\n    setNewInput(\"\"); // reset input\n  },\n  toggleTodo: ({ mergeTodos, todos }) =\u003e id =\u003e {\n    // toggle done state\n    const todo = todos[id];\n    mergeTodos({\n      [id]: {\n        ...todo,\n        done: !todo.done\n      }\n    });\n  }\n};\n\n// Components\n\nconst Todos = ({ todosByDate, newInput, changeInput, submit, toggleTodo }) =\u003e (\n  \u003cdiv\u003e\n    \u003cinput type=\"text\" value={newInput} onChange={changeInput} /\u003e\n    \u003cbutton onClick={submit}\u003eSubmit\u003c/button\u003e\n    {todosByDate.map(({ id, done, title }) =\u003e (\n      \u003cdiv key={id} onClick={() =\u003e toggleTodo(id)}\u003e\n        {title} {done \u0026\u0026 \" - done\"}\n      \u003c/div\u003e\n    ))}\n  \u003c/div\u003e\n);\n\nconst App = () =\u003e (\n  \u003cContainer\n    state={state}\n    deriveState={deriveState}\n    withHandlers={withHandlers}\n    omitProps={[\"setTodos\", \"setNewInput\", \"mergeTodos\"]}\n    render={Todos}\n  /\u003e\n);\n\n// implementation details\n\nconst uuid = require('uuid');\n\nfunction createTodo(title) {\n  const id = uuid().slice(0, 5);\n  return {\n    [id]: {\n      title,\n      id,\n      done: false,\n      stamp: Date.now()\n    }\n  };\n}\n\nexport default App;\n\n```\n\nThat's about it. Enjoy!\n\n## Usage\n\n```javascript\n\nconst propTypes = {\n  render: PropTypes.func.isRequired,\n  state: PropTypes.object.isRequired,\n  withHandlers: PropTypes.objectOf(PropTypes.func),\n  omitProps: PropTypes.arrayOf(PropTypes.string),\n  deriveState: PropTypes.arrayOf(\n    PropTypes.shape({\n      onStateChange: PropTypes.oneOfType([\n        PropTypes.arrayOf(PropTypes.string),\n        PropTypes.string\n      ]).isRequired,\n      derive: PropTypes.func.isRequired\n    })\n  )\n};\n\n```\n\n\n# Development\n\n`react-with-state-props` is build in Typescript.\nPR and Issues welcomed!\n\n# Inspirations\n\n* Andrew Clark's [recompose](https://github.com/acdlite/recompose) library\n* Kent C. Dodds Advanced React Component Patterns [Egghead course](https://egghead.io/courses/advanced-react-component-patterns)\n* Never Write Another HOC [talk](https://www.youtube.com/watch?v=BcVAq3YFiuc) by Michael Jackson\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcollardeau%2Freact-with-state-props","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcollardeau%2Freact-with-state-props","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcollardeau%2Freact-with-state-props/lists"}