100 Counter Apps -state management
|
... |
|
Different |
|
Counter |
|
Implementations |
|
... |
|
import { app, main, h1, button } from "hyperapp.V2" |
|
|
|
app({ |
|
init: 0, |
|
view: state => main({}, |
|
h1({}, state), |
|
button({ onclick: state => state - 1, disabled: state <= 0 }, "ー"), |
|
button({ onclick: state => state + 1 }, "+")), |
|
container: document.body |
|
}) |
|
import { NumberValue } from 'react-values' |
|
|
|
const Counter = () => ( |
|
<NumberValue defaultValue={0}> |
|
{({ value, increment, decrement }) => ( |
|
<button onClick={() => increment()}>+1</button> |
|
<span>{value}</span> |
|
<button onClick={() => decrement()}>-1</button> |
|
)} |
|
</NumberValue>) |
|
m.mount(document.body, { |
|
count : 0, |
|
view : (vnode) => m("div", |
|
m("div", "Count: ", vnode.state.count), |
|
m("button", { onclick : () => vnode.state.count++ }, "+"), |
|
m("button", { onclick : () => vnode.state.count-- }, "-") |
|
) |
|
}) |
|
import { Provider, Subscribe } from 'react-contextual' |
|
|
|
const store = { |
|
count: 0, |
|
up: () => state => ({ count: state.count + 1 }), |
|
down: () => state => ({ count: state.count - 1 }), |
|
} |
|
|
|
const App = () => ( |
|
<Provider {...store}> |
|
<Subscribe> |
|
{props => ( |
|
<div> |
|
<h1>{props.count}</h1> |
|
<button onClick={props.up}>Up</button> |
|
<button onClick={props.down}>Down</button> |
|
</div> |
|
)} |
|
</Subscribe> |
|
</Provider> |
|
) |
|
import React from 'react' |
|
|
|
export const init = count => count |
|
|
|
const Action = { |
|
Increment: x => x + 1, |
|
Decrement: x => x - 1 |
|
} |
|
|
|
export const update = (action, model) => action(model) |
|
|
|
export const view = (signal, model) => ( |
|
<div> |
|
<button onClick={signal(Action.Decrement)}>-</button> |
|
<div>{model}</div> |
|
<button onClick={signal(Action.Increment)}>+</button> |
|
</div> |
|
) |
|
|
|
export default {init, update, view} |
|
import { observable } from 'mobx'; |
|
import { observer } from 'mobx-react'; |
|
import React from 'react' |
|
import {render} from 'react-dom' |
|
|
|
const state = { n: 0 }; |
|
const incr = () => state.n++; |
|
const decr = () => state.n--; |
|
|
|
const view = observer(() => |
|
<div> |
|
<h1>{state.n}</h1> |
|
<button onclick={incr}>+</button> |
|
<button onclick={decr}>-</button> |
|
</div>) |
|
|
|
render( |
|
<view />, |
|
document.querySelector('#app') |
|
); |
|
import xs from 'xstream'; |
|
import Cycle from '@cycle/xstream-run'; |
|
import {div, button, p, makeDOMDriver} from '@cycle/dom'; |
|
|
|
function main(sources) { |
|
let action$ = xs.merge( |
|
sources.DOM.select('.decrement').events('click').map(ev => -1), |
|
sources.DOM.select('.increment').events('click').map(ev => +1) |
|
); |
|
|
|
let count$ = action$.fold((x,y) => x + y, 0); |
|
|
|
return { |
|
DOM: count$.map(count => |
|
div([ |
|
button('.decrement', 'Decrement'), |
|
button('.increment', 'Increment'), |
|
p('Counter: ' + count) |
|
]) |
|
) |
|
}; |
|
} |
|
|
|
Cycle.run(main, { |
|
DOM: makeDOMDriver('#main-container') |
|
}); |
|
import React, { Component, PropTypes } from 'react' |
|
import ReactDOM from 'react-dom' |
|
|
|
const increment = ({ count }) => ({ count: count + 1 }); |
|
const decrement = ({ count }) => ({ count: count - 1 }); |
|
|
|
class Counter extends React.Component { |
|
constructor(props) { |
|
super(props); |
|
this.state = { count: 0 }; |
|
} |
|
|
|
render() { |
|
return ( |
|
<p> |
|
{this.state.count} |
|
<button onClick={() => this.setState(increment)}>+</button> |
|
<button onClick={() => this.setState(decrement)}>-</button> |
|
</p> |
|
); |
|
} |
|
} |
|
|
|
ReactDOM.render(<Counter />, document.getElementById('root')); |
|
var html = require('./html') |
|
var app = require('./')() |
|
|
|
app.model(function (state, bus) { |
|
state.count = 0 |
|
bus.on('increment', function (count) { |
|
state.count += count |
|
bus.emit('render') |
|
}) |
|
}) |
|
app.router([ '/', mainView ]) |
|
app.mount('body') |
|
|
|
function mainView (state, emit) { |
|
return html` |
|
<body> |
|
<h1>count is ${state.count}</h1> |
|
<button onclick=${onIncr}>+</button> |
|
<button onclick=${onDecr}>-</button> |
|
</body> |
|
` |
|
function onIncr() { |
|
emit('increment', 1) |
|
} |
|
function onDecr() { |
|
emit('increment', -1) |
|
} |
|
} |
|
const choo = require('../../') |
|
const html = require('../../html') |
|
|
|
const app = choo() |
|
app.model({ |
|
state: { |
|
counter: 0 |
|
}, |
|
reducers: { |
|
increment: (data, state) => ({ counter: state.counter + 1 }), |
|
decrement: (data, state) => ({ counter: state.counter - 1 }) |
|
} |
|
}) |
|
|
|
const mainView = (state, prev, send) => { |
|
return html` |
|
<main class="app"> |
|
<button onclick=${() => send('increment')}>Increment</button> |
|
<button onclick=${() => send('decrement')}>Decrement</button> |
|
<p>state.counter</p> |
|
</main> |
|
} |
|
|
|
app.router((route) => [ |
|
route('/', mainView) |
|
]) |
|
|
|
document.body.appendChild(app.start()) |
|
import Html exposing (Html, button, div, text) |
|
import Html.App as App |
|
import Html.Events exposing (onClick) |
|
|
|
main = |
|
App.beginnerProgram |
|
{ model = model |
|
, view = view |
|
, update = update |
|
} |
|
|
|
type alias Model = Int |
|
model : Model |
|
model = 0 |
|
|
|
type Msg = Increment | Decrement |
|
|
|
update : Msg -> Model -> Model |
|
update msg model = |
|
case msg of |
|
Increment -> model + 1 |
|
Decrement -> model - 1 |
|
|
|
view : Model -> Html Msg |
|
view model = |
|
div [] |
|
[ button [ onClick Decrement ] [ text "-" ] |
|
, div [] [ text (toString model) ] |
|
, button [ onClick Increment ] [ text "+" ] |
|
] |
|
module App |
|
open Elmish |
|
open Elmish.React |
|
open Fable.Helpers.React |
|
open Fable.Helpers.React.Props |
|
|
|
type Model = int |
|
type Msg = Increment| Decrement |
|
let init() : Model = 0 |
|
|
|
let update (msg:Msg) (model:Model) = |
|
match msg with |
|
| Increment -> model + 1 |
|
| Decrement -> model - 1 |
|
|
|
let view model dispatch = |
|
div [] |
|
[ button [ OnClick (fun _ -> dispatch Increment) ] [ str "+" ] |
|
div [] [ str (string model) ] |
|
button [ OnClick (fun _ -> dispatch Decrement) ] [ str "-" ] ] |
|
|
|
Program.mkSimple init update view |
|
|> Program.withReact "elmish-app" |
|
|> Program.withConsoleTrace |
|
|> Program.run |
|
import { cmd } from '../src' |
|
import { Html } from '../src/React' |
|
import * as React from 'react' |
|
|
|
export type Model = number |
|
export type Flags = Model |
|
export const flags: Flags = 0 |
|
|
|
export function init(flags: Flags): [Model, cmd.Cmd<Msg>] { |
|
return [flags, cmd.none] |
|
} |
|
|
|
export type Msg = |
|
| { type: 'Increment' } |
|
| { type: 'Decrement' } |
|
|
|
export function update(msg: Msg, model: Model): [Model, cmd.Cmd<Msg>] { |
|
switch (msg.type) { |
|
case 'Increment' : |
|
return [model + 1, cmd.none] |
|
case 'Decrement' : |
|
return [model - 1, cmd.none] |
|
} |
|
} |
|
|
|
export function view(model: Model): Html<Msg> { |
|
return dispatch => ( |
|
<div>Count: {model} |
|
<button onClick={() => dispatch({ type: 'Increment' })}>+</button> |
|
<button onClick={() => dispatch({ type: 'Decrement' })}>-</button> |
|
</div> |
|
) |
|
} |
|
import React, { Component, PropTypes } from 'react' |
|
import ReactDOM from 'react-dom' |
|
import { createStore } from 'redux' |
|
|
|
class Counter extends Component { |
|
static propTypes = { |
|
value: PropTypes.number.isRequired, |
|
onIncrement: PropTypes.func.isRequired, |
|
onDecrement: PropTypes.func.isRequired |
|
} |
|
|
|
render() { |
|
const { value, onIncrement, onDecrement } = this.props |
|
return ( |
|
<p> |
|
<button onClick={onIncrement}>+</button> |
|
<button onClick={onDecrement}>-</button> |
|
</p> |
|
) |
|
} |
|
} |
|
|
|
const counter = (state = 0, action) => { |
|
switch (action.type) { |
|
case 'INCREMENT': |
|
return state + 1 |
|
case 'DECREMENT': |
|
return state - 1 |
|
default: |
|
return state |
|
} |
|
} |
|
|
|
const store = createStore(counter) |
|
const rootEl = document.getElementById('root') |
|
|
|
const render = () => ReactDOM.render( |
|
<Counter |
|
value={store.getState()} |
|
onIncrement={() => store.dispatch({ type: 'INCREMENT' })} |
|
onDecrement={() => store.dispatch({ type: 'DECREMENT' })} |
|
/>, |
|
rootEl |
|
) |
|
|
|
render() |
|
store.subscribe(render) |
|
new Vue({ |
|
data: { count: 0 }, |
|
render (h) { |
|
return h('p', [ |
|
this.count, |
|
h('button', { on: { click: () => { this.count++ }}}, '+'), |
|
h('button', { on: { click: () => { this.count-- }}}, '-') |
|
]) |
|
} |
|
}).$mount('#app') |
|
import {html,css,createStore,component,withProp,withStore,withStyle,withMarkup} from "compo-lib"; |
|
|
|
createStore((state, action) => { |
|
switch (action.type) { |
|
case "ADD": return state + 1; |
|
case "SUB": return state - 1; |
|
default: return state; |
|
} |
|
}, 0); |
|
|
|
component( |
|
"my-counter-label", |
|
withProp("value"), |
|
withStyle( |
|
({ value }) => css` |
|
:host { |
|
color: ${value < 1 ? "red" : "black"}; |
|
} |
|
` |
|
) |
|
); |
|
|
|
component( |
|
"my-counter", |
|
withStore(({ getState, dispatch }) => ({ |
|
counter: getState(), |
|
add: () => dispatch({ type: "ADD" }), |
|
sub: () => dispatch({ type: "SUB" }) |
|
})), |
|
withMarkup( |
|
({ counter, add, sub }) => html` |
|
<div> |
|
<my-counter-label value=${counter}>${counter}</my-counter-label> |
|
<button onclick=${add}>+</button> |
|
<button onclick=${sub}>-</button> |
|
</div> |
|
` |
|
) |
|
); |
|
import Dom exposing (..) |
|
import Lens exposing (Lens) |
|
|
|
counter : Lens m Int -> Html m |
|
counter value = |
|
div [] |
|
[ button [onClick (Lens.modify value ((+) -1))] [text "-"] |
|
, textAs toString value |
|
, button [onClick (Lens.modify value ((+) 1))] [text "+"] |
|
] |
|
(def state (atom 0)) |
|
|
|
(rum/defc view < rum/reactive [state] |
|
[:div |
|
[:button {:on-click #(swap! state dec)} "-"] |
|
[:span (rum/react state)] |
|
[:button {:on-click #(swap! state inc)} "+"]]) |
|
|
|
(run/mount (view state) |
|
(js/document.getElementById "app")) |
|
import * as R from "ramda" |
|
import * as U from "karet.util" |
|
import React from "karet" |
|
import ReactDOM from "react-dom" |
|
|
|
const Counter = ({value}) => |
|
<div> |
|
<div>Count: {value}</div> |
|
<button onClick={() => value.modify(R.add(+1))}>+</button> |
|
<button onClick={() => value.modify(R.add(-1))}>-</button> |
|
</div> |
|
|
|
ReactDOM.render(<Counter value={U.atom(0)}/>, document.getElementById("app")) |
|
data Action = Increment | Decrement |
|
|
|
type State = Int |
|
update :: Action -> State -> State |
|
update Increment count = count + 1 |
|
update Decrement count = count - 1 |
|
|
|
view :: State -> Html Action |
|
view count = |
|
div |
|
[] |
|
[ button [ onClick (const Increment) ] [ text "Increment" ] |
|
, span [] [ text (show count) ] |
|
, button [ onClick (const Decrement) ] [ text "Decrement" ] |
|
] |
|
<p> |
|
{{ count }} |
|
<button on:click="set({count: count + 1})">+</button> |
|
<button on:click="set({count: count - 1})">-</button> |
|
</p> |
|
|
|
<script> |
|
export default { |
|
data() { |
|
return { |
|
count: 0 |
|
} |
|
} |
|
} |
|
</script> |
|
import * as React from 'react' |
|
import * as ReactDOM from 'react-dom' |
|
import { Atom, F } from '@grammarly/focal' |
|
|
|
const Counter = (props: { count: Atom<number> }) => |
|
<F.div> |
|
You have clicked this button {props.count} time(s). |
|
<button onClick={() => props.count.modify(x => x + 1)}>Click again?</button> |
|
</F.div> |
|
|
|
const App = (props: { state: Atom<{ count: number }> }) => |
|
<div> |
|
<Counter count={ props.state.lens(x => x.count)} /> |
|
</div> |
|
|
|
ReactDOM.render(<App state={Atom.create({ count: 0 })} />, document.getElementById('app')) |
|
import {Block, run} from 'cyclow' |
|
|
|
const Counter = () => Block({ |
|
on: { |
|
'in.init': () => counter => 0, |
|
'dom.increment': () => counter => counter + 1, |
|
'dom.decrement': () => counter => counter - 1 |
|
}, |
|
view: counter => ({tag:'div#app', content: [ |
|
{tag: 'div.counter', content: `${counter}`}, |
|
{tag: 'div.buttons', content: [ |
|
{tag: 'button', on: {click: 'decrement'}, content: '-'}, |
|
{tag: 'button', on: {click: 'increment'}, content: '+'} |
|
]} |
|
]}) |
|
}) |
|
|
|
run(Counter, {target: 'app'}) |
|
import React from 'react' |
|
import { store, view } from 'react-easy-state' |
|
|
|
const counter = store({ |
|
num: 0, |
|
incr: () => counter.num++, |
|
decr: () => counter.num-- |
|
}) |
|
|
|
export default view(() => <> |
|
<div>Count: {value}</div> |
|
<button onClick={counter.incr}>+</button> |
|
<button onClick={counter.decr}>-</button> |
|
</>) |
|
import { withState } from `recompose` |
|
|
|
const enhance = withState('counter', 'setCounter', 0) |
|
|
|
const Counter = enhance(({ counter, setCounter }) => |
|
<div> |
|
Count: {counter} |
|
<button onClick={() => setCounter(n => n + 1)}>Increment</button> |
|
<button onClick={() => setCounter(n => n - 1)}>Decrement</button> |
|
</div> |
|
) |
|
import React, { useState } from "react" |
|
import ReactDOM from "react-dom" |
|
|
|
function App() { |
|
const [count, setCount] = useState(0) |
|
|
|
return ( |
|
<p> |
|
{count} |
|
<button onClick={() => setCount(count + 1)}>+</button> |
|
<button onClick={() => setCount(count - 1)}>-</button> |
|
</p> |
|
) |
|
} |
|
|
|
ReactDOM.render(<App />, document.getElementById("root")) |
|
import { html } from 'https://unpkg.com/lit-html/lit-html.js'; |
|
import { component, useState } from 'https://unpkg.com/@matthewp/haunted/haunted.js'; |
|
|
|
function Counter() { |
|
const [count, setCount] = useState(0); |
|
|
|
return html` |
|
<div id="count">${count}</div> |
|
<button type="button" @click=${() => setCount(count + 1)}>Increment</button> |
|
<button type="button" @click=${() => setCount(count - 1)}>Decrement</button> |
|
`; |
|
} |
|
|
|
customElements.define('my-counter', component(Counter)); |
|
import { useState } from '...' |
|
|
|
const App = () => |
|
useState(0) |
|
.map(([count, setCount]) => ( |
|
<div> |
|
{count} |
|
<button onClick={() => setCount(count + 1)}>+</button> |
|
<button onClick={() => setCount(count - 1)}>-</button> |
|
</div> |
|
)) |
|
|
|
render(<App />, main) |
|
import React from "react"; |
|
import ReactDOM from "react-dom"; |
|
import { Observable } from "rxjs"; |
|
import { map } from 'rxjs/operators'; |
|
import { observeComponent } from "./observe"; |
|
|
|
function state(defaultState) { |
|
return new Observable((observer) => { |
|
let state; |
|
|
|
function setState(newState) { |
|
if (typeof newState === 'function') { |
|
newState = newState(state); |
|
} |
|
observer.next([state = newState, setState]); |
|
} |
|
setState(defaultState); |
|
}); |
|
} |
|
|
|
const App = observeComponent(() => ( |
|
state(0).pipe( |
|
map(([count, updateCount]) => ( |
|
<div className="App"> |
|
<h2> {count} </h2> |
|
<button onClick={() => updateCount(c => c - 1)}>Decrement</button> |
|
<button onClick={() => updateCount(c => c + 1)}>Increment</button> |
|
</div> |
|
)) |
|
) |
|
)); |
|
|
|
const rootElement = document.getElementById("root"); |
|
ReactDOM.render(<App />, rootElement); |
|
const initialState = { count: 0 }; |
|
|
|
function reducer(draft, action) { |
|
switch (action.type) { |
|
case "increment": return void draft.count++; |
|
case "decrement": return void draft.count--; |
|
} |
|
} |
|
|
|
function Counter() { |
|
const [state, dispatch] = useImmerReducer(reducer, initialState); |
|
return ( |
|
<> |
|
Count: {state.count} |
|
<button onClick={() => dispatch({ type: "increment" })}>+</button> |
|
<button onClick={() => dispatch({ type: "decrement" })}>-</button> |
|
</> |
|
); |
|
} |
|
import { define, render } from 'omi' |
|
|
|
define('my-counter', function() { |
|
const [count, setCount] = this.useData(0) |
|
this.useCss(`button{ color: red; }`) |
|
|
|
return ( |
|
<div> |
|
<button onClick={() => setCount(count - 1)}>-</button> |
|
<span>{count}</span> |
|
<button onClick={() => setCount(count + 1)}>+</button> |
|
</div> |
|
) |
|
}) |
|
|
|
render(<my-counter />, 'body') |
|
module Counter where |
|
|
|
import Prelude |
|
import React.Basic (Component, JSX, StateUpdate(..), capture_, createComponent, make) |
|
import React.Basic.DOM as R |
|
|
|
component :: Component Props |
|
component = createComponent "Counter" |
|
|
|
type Props = { label :: String } |
|
|
|
data Action = Increment |
|
|
|
counter :: Props -> JSX |
|
counter = make component { initialState, update, render } |
|
where |
|
initialState = { counter: 0 } |
|
|
|
update self = case _ of |
|
Increment -> Update self.state { counter = self.state.counter + 1 } |
|
|
|
render self = |
|
R.button |
|
{ onClick: capture_ self Increment |
|
, children: [ R.text (self.props.label <> ": " <> show self.state.counter) ] |
|
} |
|
const App = () => |
|
useState(0) |
|
.map(([count, setCount]) => ( |
|
<div> |
|
{count} |
|
<button onClick={() => setCount(count + 1)}> |
|
+ |
|
</button> |
|
</div> |
|
)) |
|
|
|
render(<App />, main) |
|
const state = 0; |
|
|
|
const view = state => { |
|
return `<div> |
|
<h1>${state}</h1> |
|
<button onclick='app.run("-1")'>-1</button> |
|
<button onclick='app.run("+1")'>+1</button> |
|
</div>`; |
|
}; |
|
|
|
const update = { |
|
'+1': state => state + 1, |
|
'-1': state => state - 1 |
|
}; |
|
|
|
app.start('my-app', state, view, update); |
|
import { StoreProvider, useStore } from './store' |
|
|
|
const Counter = () => { |
|
const [state, setState] = useStore(); |
|
|
|
const increment = () => |
|
setState(old => ({...old, count: old.count + 1})); |
|
|
|
const decrement = () => |
|
setState(old => ({...old, count: old.count - 1})); |
|
|
|
return ( |
|
<div> |
|
<button onClick={decrement}>-</button> |
|
{state.count} |
|
<button onClick={increment}>+</button> |
|
</div> |
|
); |
|
}; |
|
|
|
const App = () => { |
|
return ( |
|
<StoreProvider initialValue={{ count: 0 }}> |
|
<Counter /> |
|
</StoreProvider> |
|
); |
|
}; |
|
const useIncrement = () => { |
|
const [state, setState] = useStore(); |
|
|
|
return () => |
|
setState(old => ({ |
|
...old, |
|
count: old.count + 1 |
|
})); |
|
} |
|
|
|
const useDecrement = () => { |
|
const [state, setState] = useStore(); |
|
|
|
return () => |
|
setState(old => ({ |
|
...old, |
|
count: old.count + 1 |
|
})); |
|
} |
|
|
|
const Counter = () => { |
|
const [{ count }] = useStore() |
|
const increment = useIncrement() |
|
const decrement = useDecrement() |
|
|
|
return ( |
|
<div> |
|
<button onClick={decrement}>-</button> |
|
{count} |
|
<button onClick={increment}>+</button> |
|
</div> |
|
); |
|
}; |
|
import React, { useState, useContext } from "react"; |
|
import ReactDOM from "react-dom"; |
|
import createContainer from "constate"; |
|
|
|
function useCounter() { |
|
const [count, setCount] = useState(0); |
|
const increment = () => setCount(count + 1); |
|
const decrement = () => setCount(count - 1); |
|
return { count, increment, decrement }; |
|
} |
|
|
|
const CounterContainer = createContainer(useCounter); |
|
|
|
function ButtonIncr() { |
|
const { increment } = useContext(CounterContainer.Context); |
|
return <button onClick={increment}>+</button>; |
|
} |
|
function ButtonDecr() { |
|
const { decrement } = useContext(CounterContainer.Context); |
|
return <button onClick={decrement}>-</button>; |
|
} |
|
|
|
function Count() { |
|
const { count } = useContext(CounterContainer.Context); |
|
return <span>{count}</span>; |
|
} |
|
|
|
const Counter = () => |
|
<CounterContainer.Provider> |
|
<Count /> |
|
<ButtonIncr /> |
|
<ButtonDecr /> |
|
</CounterContainer.Provider> |
|
|
|
|
|
const rootElement = document.getElementById("root"); |
|
ReactDOM.render(<Counter />, rootElement); |
|
const { useState } = React; |
|
const html = htm.bind(React.createElement); |
|
|
|
const App = () => { |
|
const [count, setCount] = useState(0); |
|
const increment = () => setCount(count+1); |
|
const decrement = () => setCount(count-1); |
|
|
|
return html` |
|
<div class="app"> |
|
<button onClick=${decrement}> - </button> |
|
<strong> ${count} </strong> |
|
<button onClick=${increment}> + </button> |
|
</div> |
|
</div> |
|
`; |
|
}; |
|
|
|
ReactDOM.render(html`<${App} />`, document.getElementById('root')); |
|
const initialState = { count: 0 }; |
|
|
|
function reducer(state, action) { |
|
switch (action.type) { |
|
case 'increment': |
|
return { count: state.count + 1 }; |
|
case 'decrement': |
|
return { count: state.count - 1 }; |
|
} |
|
} |
|
|
|
function Counter() { |
|
const [ state, dispatch ] = useReducer(reducer, initialState); |
|
const increment = () => dispatch({ type: 'increment' }); |
|
const decrement = () => dispatch({ type: 'decrement' }); |
|
return ( |
|
<> |
|
Count: {state.count} |
|
<button onClick={increment}>+</button> |
|
<button onClick={decrement}>-</button> |
|
</> |
|
); |
|
} |
This comment has been minimized.
mweststrate commentedNov 19, 2016
Suggestion, MobX + React :) ? That is a bit more straight-forward compared to VirtualDOM