Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/collardeau/react-with-state-props
A container render-prop component to initialize, handle and derive state in React.
https://github.com/collardeau/react-with-state-props
react react-component state-props typescript
Last synced: about 2 months ago
JSON representation
A container render-prop component to initialize, handle and derive state in React.
- Host: GitHub
- URL: https://github.com/collardeau/react-with-state-props
- Owner: collardeau
- Created: 2018-03-05T16:14:39.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2018-03-30T10:26:44.000Z (almost 7 years ago)
- Last Synced: 2024-05-21T00:59:05.074Z (7 months ago)
- Topics: react, react-component, state-props, typescript
- Language: TypeScript
- Homepage:
- Size: 713 KB
- Stars: 11
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- awesome-react-render-props - react-senna
README
[![Build Status](https://travis-ci.org/collardeau/react-with-state-props.svg?branch=master)](https://travis-ci.org/collardeau/react-with-state-props)
[![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)
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)# react-with-state-props
A container render-prop component to initialize, handle and derive state in React.
## Installation
`npm install react-with-state-props --save`
## Examples
Create some state:
```javascript
import Container from "react-with-state-props"// ...
{
// props ready-to-go based on the state you provided:
console.log(props);
// { counter: 0, setCounter: [Function] }
// props.setCounter is automatically generated
return ; // whatever JSX/Comp you want
}}
/>;
```You can derive state (and keep your original state as simple as possible):
```javascript
({
isOdd: Boolean(state.counter % 2)
})
}
]}
render={props => {
// { counter: 0, setCounter: [Function], isOdd: false }
return ; // your JSX
}}
/>;// You can derive state from derived state:
({
isOdd: Boolean(counter % 2)
})
},
{
onStateChange: ["isOdd"], // now react to `isOdd` changes
derive: ({ isOdd }) => ({
isEven: !isOdd
})
}
]}
render={props => {
// { counter: 0, setCounter: [Function], isOdd: true, isEven: false }
return ; // your JSX
}}
/>;```
You can also create custom state handlers:
```javascript
() => {
// props.setCounter was created in Container
props.setCounter(props.counter + 1)
}
}}
render={props => {
console.log(props);
// { counter: 0, add1: [Function], setCounter: [Function] }
return ; // your JSX
}}
/>;// another example with multiple handlers and some syntax shorthand
// where we want to render a counter that we can increment by 1 or 10, or reset:num => setCounter(counter + num),
incrBy1: ({ incr }) => () => incr(1), // using custom handler just defined
incrBy10: ({ incr }) => () => incr(10),
reset: ({ setCounter }) => () => setCounter(0)
}}
omitProps={["setCounter", "incr"]} // drop props before the render function
render={props => {
console.log(props);
// { counter: 0, incrBy1: [Function], incrBy10: [Function], reset: [Function] }
return ; // your JSX
}}
/>;```
**Putting it all together**, here is a basic Todo App, with the ability to create todos and toggle them between done/undone.
We keep the todos in an object for easier lookup (by key), and derive an array of todos on changes that we render:```javascript
import React from "react";
import Container from "react-with-state-props";// define state, derived state and state handlers
const state = {
todos: {},
newInput: ""
};const deriveState = [
{
onStateChange: "todos", // when `state.todos` change
derive: ({ todos }) => ({
// derive `todosByDate` array
todosByDate: Object.keys(todos)
.map(key => todos[key])
.sort((a, b) => b.stamp - a.stamp)
})
}
];const withHandlers = {
changeInput: ({ setNewInput }) => e => {
// controlled text input
setNewInput(e.target.value); // setNewInput is created from `newInput` state
},
mergeTodos: ({ setTodos, todos }) => newTodos => {
// other handlers will use this
setTodos({ ...todos, ...newTodos }); // setTodos is created from `todos` state
},
submit: ({ mergeTodos, setNewInput, newInput }) => () => {
// submit new todo
if (!newInput) return;
const title = newInput.trim();
mergeTodos(createTodo(title));
setNewInput(""); // reset input
},
toggleTodo: ({ mergeTodos, todos }) => id => {
// toggle done state
const todo = todos[id];
mergeTodos({
[id]: {
...todo,
done: !todo.done
}
});
}
};// Components
const Todos = ({ todosByDate, newInput, changeInput, submit, toggleTodo }) => (
Submit
{todosByDate.map(({ id, done, title }) => (
toggleTodo(id)}>
{title} {done && " - done"}
))}
);const App = () => (
);// implementation details
const uuid = require('uuid');
function createTodo(title) {
const id = uuid().slice(0, 5);
return {
[id]: {
title,
id,
done: false,
stamp: Date.now()
}
};
}export default App;
```
That's about it. Enjoy!
## Usage
```javascript
const propTypes = {
render: PropTypes.func.isRequired,
state: PropTypes.object.isRequired,
withHandlers: PropTypes.objectOf(PropTypes.func),
omitProps: PropTypes.arrayOf(PropTypes.string),
deriveState: PropTypes.arrayOf(
PropTypes.shape({
onStateChange: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.string),
PropTypes.string
]).isRequired,
derive: PropTypes.func.isRequired
})
)
};```
# Development
`react-with-state-props` is build in Typescript.
PR and Issues welcomed!# Inspirations
* Andrew Clark's [recompose](https://github.com/acdlite/recompose) library
* Kent C. Dodds Advanced React Component Patterns [Egghead course](https://egghead.io/courses/advanced-react-component-patterns)
* Never Write Another HOC [talk](https://www.youtube.com/watch?v=BcVAq3YFiuc) by Michael Jackson