-
-
Save srdjan/1d10cbd42a2d695f696dee6b47fdc5e0 to your computer and use it in GitHub Desktop.
100+ different js counter apps... |
import { h, app } from "hyperapp" | |
app({ | |
init: 0, | |
view: state => ( | |
<main> | |
<h1>{state}</h1> | |
<button onclick={state => state - 1}>-</button> | |
<button onclick={state => state + 1}>+</button> | |
</main> | |
), | |
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> | |
</> | |
); | |
} |
import { _, component, invalidate, render } from "ivi"; | |
import { h1 } from "ivi-html"; | |
const Counter = component((c) => { | |
let counter = 0; | |
const ticker = useEffect(c, (interval) => { | |
const id = setInterval(() => { | |
counter++; | |
invalidate(c); | |
}, interval); | |
return () => clearInterval(id); | |
}); | |
return (interval) => ( | |
ticker(interval), | |
div(_, _, `Counter: ${counter}`), | |
); | |
}); | |
render( | |
Counter(1000), | |
document.getElementById("app"), | |
); |
<!doctype html> | |
<html lang="en"> | |
<my-counter></my-counter> | |
<script type="module"> | |
import { html } from 'https://unpkg.com/lit-html/lit-html.js'; | |
import { component, useState } from 'https://unpkg.com/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)); | |
</script> |
import createStore from 'storeon' | |
import { StoreContext, connect } from 'storeon/react' | |
let increment = store => { | |
store.on('@init', () => ({ count: 0 })) | |
store.on('inc', ({ count }) => ({ count: count + 1 })) | |
} | |
const store = createStore([increment]) | |
const Counter = connect('count', React.memo( | |
({ count, dispatch }) => | |
<> | |
{count} | |
<button onClick={() => dispatch('inc')} /> | |
<button onClick={() => dispatch('dec')} /> | |
</> | |
) | |
render( | |
<StoreContext.Provider value={store}> | |
<Counter/> | |
</StoreContext.Provider>, | |
document.body | |
) |
import { Component, FastDomNode, createComponent, fdObject, fdValue } from "../../src"; | |
export function createCounter() { | |
return createComponent(Counter); | |
} | |
class Counter extends Component { | |
width = 100; | |
reactive = { | |
counter: fdValue(0), | |
} | |
get counter() { | |
return this.reactive.counter; | |
} | |
onClick = () => { | |
this.counter.value += 1; | |
} | |
template: FastDomNode = { | |
tag: "button", | |
textValue: this.counter, | |
listeners: { | |
click: this.onClick | |
} | |
} |
[@react.component] | |
let counter = () => { | |
<div> | |
{ReasonReact.string("Count: " ++ string_of_int(state.count)} | |
<> | |
<Button label="Increment" onClick={_event => state.count++)} /> | |
<Button label="Decrement" onClick={_event => state.count--} /> | |
</> | |
</div>; | |
}; |
import React from "react"; | |
import ReactDOM from "react-dom"; | |
import { createStore, createEvent, createApi } from "effector"; | |
import { useStore } from "effector-react"; | |
const $counter = createStore(0) | |
const increment = createEvent(); | |
const decrement = createEvent(); | |
$counter.on(increment, state => state + 1) | |
$counter.on(decrement, state => state - 1) | |
function Counter () { | |
const counter = useStore($counter) | |
return ( | |
<> | |
<div>{counter}</div> | |
<button onClick={increment}>+</button> | |
<button onClick={decrement}>-</button> | |
</> | |
) | |
}; | |
ReactDOM.render(<Counter />, document.body); |
import React, { useState } from "react" | |
import { createContainer } from "unstated-next" | |
import { render } from "react-dom" | |
function useCounter() { | |
let [count, setCount] = useState(0) | |
let decrement = () => setCount(count - 1) | |
let increment = () => setCount(count + 1) | |
return { count, decrement, increment } | |
} | |
let Counter = createContainer(useCounter) | |
function CounterDisplay() { | |
let counter = Counter.useContainer() | |
return ( | |
<div> | |
<button onClick={counter.decrement}>-</button> | |
<span>{counter.count}</span> | |
<button onClick={counter.increment}>+</button> | |
</div> | |
) | |
} | |
function App() { | |
return ( | |
<Counter.Provider> | |
<CounterDisplay /> | |
<CounterDisplay /> | |
</Counter.Provider> | |
) | |
} | |
render(<App />, document.getElementById("root")) |
import { html, define } from 'hybrids'; | |
export function increaseCount(host) => host.count += 1 | |
export function decreaseCount(host) => host.count -= 1 | |
export const SimpleCounter = { | |
count: 0, | |
render: ({ count }) => html` | |
Count: ${count} | |
<button onclick="${increaseCount}"/> | |
<button onclick="${decreaseCount}"/> | |
`, | |
}; | |
define('simple-counter', SimpleCounter); |
import { html } from 'hybrids'; | |
import store, { increment, decrement } from './redux'; | |
const connect = (store, mapState) => ({ | |
get: mapState ? () => mapState(store.getState()) : () => store.getState(), | |
connect: (host, key, invalidate) => store.subscribe(invalidate), | |
}); | |
const onInc = ({ offset }) => store.dispatch(increment(offset)); | |
const onDec = ({ offset }) => store.dispatch(decrement(offset)); | |
export default { | |
count: connect(store, (state) => state.count), | |
offset: 1, | |
render: ({ count, offset }) => html` | |
<style>:host { display: block; }</style> | |
<button onclick="${onInc}">+${offset}</button> | |
<button onclick="${onDec}">-${offset}</button> | |
<p>Count: ${count}</p> | |
`, | |
}; |
import { define } from 'hybrids'; | |
import React from 'react'; | |
import ReactDOM from 'react-dom'; | |
import Counter from './Counter'; | |
function reactify(fn) { | |
return (host) => { | |
const Component = fn(host); | |
return (host, target) => ReactDOM.render(Component, target); | |
} | |
} | |
const ReactCounter = { | |
count: 0, | |
render: reactify(({ count }) => | |
<Counter count={count} /> | |
), | |
}; | |
define('react-counter', ReactCounter); |
import React from "react"; | |
import { ReComponent, Update } from "react-recomponent"; | |
class Counter extends ReComponent { | |
constructor() { | |
super(); | |
this.handleClick = this.createSender("CLICK"); | |
this.state = { count: 0 }; | |
} | |
static reducer(action, state) { | |
switch (action.type) { | |
case "CLICK": | |
return Update({ count: state.count + 1 }); | |
} | |
} | |
render() { | |
return ( | |
<button onClick={this.handleClick}> | |
You’ve clicked this {this.state.count} times(s) | |
</button> | |
); | |
} | |
} |
import React from 'react'; | |
import { createGlobalState } from 'react-hooks-global-state'; | |
const initialState = { counter: 0 }; | |
const { GlobalStateProvider, useGlobalState } = createGlobalState(initialState); | |
const Counter = () => { | |
const [value, update] = useGlobalState('counter'); | |
return ( | |
<div> | |
<span>Counter: {value}</span> | |
<button onClick={() => update(v => v + 1)}>+1</button> | |
<button onClick={() => update(v => v - 1)}>-1</button> | |
</div> | |
); | |
}; | |
const App = () => ( | |
<GlobalStateProvider> | |
<Counter /> | |
<Counter /> | |
</GlobalStateProvider> | |
); |
<script type="module"> | |
import Litedom from '//unpkg.com/litedom'; | |
Litedom({ | |
el: '#counterWidget', | |
data: { | |
count: 0 | |
}, | |
up(event) { | |
this.data.count++; | |
}, | |
down(event) { | |
this.data.count--; | |
} | |
}); | |
</script> | |
<div id="counterWidget"> | |
<div>Count: {this.count}</div> | |
<button @click="down">DOWN</button> | |
<button @click="up">UP</button> | |
</div> | |
import React, { useReducer } from 'react'; | |
import ReactDOM from 'react-dom'; | |
import { Provider, useTracked } from 'react-tracked'; | |
const initialState = { | |
counter: 0 | |
}; | |
const reducer = (state, action) => { | |
switch (action.type) { | |
case 'increment': return { ...state, counter: state.counter + 1 }; | |
case 'decrement': return { ...state, counter: state.counter - 1 }; | |
default: throw new Error(`unknown action type: ${action.type}`); | |
} | |
}; | |
const useValue = () => useReducer(reducer, initialState); | |
const Counter = () => { | |
const [state, dispatch] = useTracked(); | |
return ( | |
<div> | |
<span>Count:{state.counter}</span> | |
<button type="button" onClick={() => dispatch({ type: 'increment' })}>+1</button> | |
<button type="button" onClick={() => dispatch({ type: 'decrement' })}>-1</button> | |
</div> | |
); | |
}; | |
const App = () => ( | |
<Provider useValue={useValue}> | |
<Counter /> | |
</Provider> | |
); | |
ReactDOM.render(<App />, document.getElementById('app')); |
/* eslint-env browser */ | |
import React, { useState, StrictMode } from "react"; | |
import ReactDOM from "react-dom"; | |
import { Provider, useTracked } from "react-tracked"; | |
import produce from "immer"; | |
const useValue = () => | |
useState({ | |
count: 0 | |
}); | |
const Counter = () => { | |
const [state, setState] = useTracked(); | |
const increment = () => { | |
setState( | |
produce(draft => { | |
draft.count += 1; | |
}) | |
); | |
}; | |
const decrement = () => { | |
setState( | |
produce(draft => { | |
draft.count -= 1; | |
}) | |
); | |
}; | |
return ( | |
<div> | |
<div>Count: {state.count}</div> | |
<button type="button" onClick={increment}>+</button> | |
<button type="button" onClick={decrement}>-</button> | |
</div> | |
); | |
}; | |
const App = () => ( | |
<StrictMode> | |
<Provider useValue={useValue}> | |
<Counter /> | |
</Provider> | |
</StrictMode> | |
); | |
ReactDOM.unstable_createRoot(document.getElementById("app")).render(<App />); |
import React from 'react'; | |
import { createStore } from 'redux'; | |
import { | |
Provider, | |
useDispatch, | |
useTrackedState, | |
} from 'reactive-react-redux'; | |
const initialState = { | |
count: 0 | |
}; | |
const reducer = (state = initialState, action) => { | |
switch (action.type) { | |
case 'increment': return { ...state, count: state.count + 1 }; | |
case 'decrement': return { ...state, count: state.count - 1 }; | |
default: return state; | |
} | |
}; | |
const store = createStore(reducer); | |
const Counter = () => { | |
const state = useTrackedState(); | |
const dispatch = useDispatch(); | |
return ( | |
<div> | |
<span>Count: {state.count}</span> | |
<button type="button" onClick={() => dispatch({ type: 'increment' })}>+1</button> | |
<button type="button" onClick={() => dispatch({ type: 'decrement' })}>-1</button> | |
</div> | |
); | |
}; | |
const App = () => ( | |
<Provider store={store}> | |
<Counter /> | |
</Provider> | |
); |
import React from "react"; | |
import ReactDOM from "react-dom"; | |
import { createStore, useStore } from "react-hookstore"; | |
createStore("counter", 0); | |
export function Counter() { | |
const [state, setState] = useStore("counter"); | |
return ( | |
<div> | |
<h2>Count: {state}</h2> | |
<button type="button" onClick={() => setState(state + 1)}>Inc</button> | |
<button type="button" onClick={() => setState(state - 1)}>Dec</button> | |
</div> | |
); | |
} | |
ReactDOM.render( | |
<React.Fragment> | |
<Counter /> | |
</React.Fragment>, | |
document.querySelector("#root") | |
); |
import React from "react"; | |
import ReactDOM from "react-dom"; | |
import {ReusableProvider, createStore} from 'reusable'; | |
import {useState} from 'react'; | |
const useCounter = createStore(() => useState(0)); | |
export function Counter() { | |
const [counter] = useCounter(); | |
return ( | |
<div> | |
{counter} | |
</div> | |
); | |
} | |
function App() { | |
return ( | |
<ReusableProvider> | |
<Counter/> | |
</ReusableProvider> | |
); | |
} | |
const rootElement = document.getElementById("root"); | |
ReactDOM.render(<App />, rootElement); |
import create from 'zustand' | |
const [useStore] = create(set=> ({ | |
count: 1, | |
inc: () => set(state => ({ count: state.count + 1 })), | |
dec: () => set(state => ({ count: state.count - 1 })) | |
})) | |
function Counter() { | |
const {count, inc, dec} = useStore() | |
return ( | |
<> | |
<h1>{count}</h1> | |
<button onClick={inc}>inc</button> | |
<button onClick={dec}>dec</button> | |
</> | |
) | |
} |
import React, { useDispatch } from 'reactn' | |
const incr = (count, action) => count + action.value | |
const decr = (count, action) => count - action.value | |
const MyComponent = () => { | |
const increment = useDispatch(incr, 'count') | |
const decrement = useDispatch(decr, 'count') | |
return ( | |
<div> | |
<button onClick={() => increment({ value: 1 })}>incr</button> | |
<button onClick={() => decrement({ value: 1 })}>decr</button> | |
</div> | |
) | |
} | |
export default MyComponent |
import haunted, { useState } from 'https://unpkg.com/haunted/core.js?module'; | |
import { html, render } from 'https://unpkg.com/lighterhtml?module'; | |
const { component } = haunted({ | |
render: (what, where) => render(where, () => what) | |
}); | |
function Counter() { | |
let [count, set] = useState(0); | |
return html` | |
<h1>Counter</h1> | |
<p>count: <strong>${count}</strong></p> | |
<button onclick=${() => set(count + 1)} type="button">Increment</button> | |
<button onclick=${() => set(count - 1)} type="button">Decrement</button> | |
`; | |
} | |
customElements.define('my-counter', component(Counter)); |
(function (angular) { | |
// service | |
var CounterService = (function () { | |
function CounterService() { | |
}; | |
CounterService.prototype.count = function (object, property, from, to, duration, effect, step, finish) { | |
var target = {}; | |
// stop previous animation | |
$(object).stop(true, true); | |
object[property] = parseFloat(from || 0); | |
target[property] = parseFloat(to || 0); | |
if (object[property] == target[property]) return; | |
$(object).animate(target, { | |
duration: duration, | |
easing: effect, | |
step: step | |
}).promise().done(function () { | |
if (angular.isFunction(finish)) finish(); | |
}); | |
}; | |
return CounterService; | |
})(); | |
// directive | |
var CounterDirective = (function () { | |
function CounterDirective(counter, timeout) { | |
this.restrict = 'EAC'; | |
this.scope = { | |
to: '=', | |
value: '=', | |
effect: '=?', | |
duration: '=?', | |
finish: '&?' | |
}; | |
$counter = counter; | |
$timeout = timeout; | |
}; | |
CounterDirective.prototype.$inject = ['$counter', '$timeout']; | |
CounterDirective.prototype.link = function ($scope, $element, $attrs, $controller) { | |
var defaults = { | |
effect: 'linear', | |
duration: 1000 | |
}; | |
if (!angular.isDefined($scope.to)) | |
throw new 'Angular Counter: attribute `to` is undefined'; | |
angular.forEach(defaults, function (value, key) { | |
if (!angular.isDefined($scope[key])) $scope[key] = defaults[key]; | |
}); | |
$scope.step = function (value) { | |
$timeout(function () { | |
$scope.$apply(function () { | |
$scope.value = value; | |
}); | |
}); | |
}; | |
$scope.$watch('to', function () { | |
$counter.count($scope, 'value', $scope.value, $scope.to, $scope.duration, $scope.effect, $scope.step, $scope.finish); | |
}); | |
}; | |
return CounterDirective; | |
})(); | |
angular | |
.module('counter', []) | |
.service('$counter', function () { | |
return new CounterService(); | |
}) | |
.directive('counter', ['$counter', '$timeout', function ($counter, $timeout) { | |
return new CounterDirective($counter, $timeout); | |
}]); |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Document</title> | |
</head> | |
<body> | |
<script type="module"> | |
import { h, app } from "https://unpkg.com/hyperapp" | |
app({ | |
init: 0, | |
view: state => | |
h("div", {}, [ | |
h("h1", {}, state), | |
h("button", { onClick: state => state - 1 }, "-"), | |
h("button", { onClick: state => state + 1 }, "+") | |
]), | |
node: document.getElementById("app") | |
}) | |
</script> | |
<div class="app"></div> | |
</body> | |
</html> |
counter id = component \on -> do | |
count <- accum (+) 0 on.change | |
( H.div {} ( | |
H.text "Counter " </> | |
H.span {} (H.textB $ map show count) </> | |
H.button {} (H.text "+" ) `use` (\o -> { change: o.click $> 1 }) </> | |
H.button {} (H.text "-" ) `use` (\o -> { change: o.click $> -1 }) | |
) | |
) `output` {} | |
main = runComponent "#mount" (counter 0) |
import React from 'react' | |
import { useBistate, useMutate } from 'bistate/react' | |
export default function Counter() { | |
let state = useBistate({ count: 0 }) | |
let incre = useMutate(() => { | |
state.count += 1 | |
}) | |
let decre = useMutate(() => { | |
state.count -= 1 | |
}) | |
return ( | |
<div> | |
<button onClick={incre}>+1</button> | |
{state.count} | |
<button onClick={decre}>-1</button> | |
</div> | |
) | |
} |
import * as React from 'react' | |
import * as ReactDOM from 'react-dom' | |
import { html, platform, state } from 'effe-ts' | |
type Action = { type: 'Increase' } | { type: 'Decrease' } | |
type Model = number | |
const init: state.State<Model> = state.of(0) | |
function update(action: Action, model: Model): state.State<Model> { | |
switch (action.type) { | |
case 'Increase': | |
return state.of(model + 1) | |
case 'Decrease': | |
return state.of(model - 1) | |
} | |
} | |
const view = (model: Model) => (dispatch: platform.Dispatch<Action>) => ( | |
<> | |
<span>Counter: {model}</span> | |
<button onClick={() => dispatch({ type: 'Increase' })}>+</button> | |
<button onClick={() => dispatch({ type: 'Decrease' })}>-</button> | |
</> | |
) | |
const app = html.program(init, update, view) | |
html.run(app, dom => ReactDOM.render(dom, document.getElementById('app')!), {}) |
import { render } from "preact"; | |
import flowponent from "flowponent"; | |
const App = flowponent(function*() { | |
let count = 0; | |
for (;;) { | |
count += yield step => ( | |
<div> | |
<div>current value: {count}</div> | |
<button onClick={() => step(1)}>+1</button> | |
<button onClick={() => step(-1)}>-1</button> | |
</div> | |
); | |
} | |
}); | |
render(<App />, document.getElementById("root")); |
const { X, x } = xreact | |
function xmount(component, dom) { ReactDOM.render(React.createFactory(X)({}, component), dom) } | |
const CounterView = ({actions, count}) => ( | |
<div> | |
<button onClick={actions.dec}>-</button> | |
<span>{count}</span> | |
<button onClick={actions.inc}>+</button> | |
</div> | |
) | |
CounterView.defaultProps = {count: 0} | |
const plan = (intent$) => { | |
return { | |
update$: intent$.map(intent => { | |
switch (intent.type) { | |
case 'inc': | |
return state => ({ count: state.count + 1 }); | |
case 'dec': | |
return state => ({ count: state.count - 1 }); | |
default: | |
return _ => _; | |
} | |
}), | |
actions: { | |
inc: () => ({ type: 'inc' }), | |
dec: () => ({ type: 'dec' }) | |
} | |
} | |
} | |
const Counter = x(plan)(CounterView); | |
xmount(<Counter/>, | |
document.getElementById('app') | |
); |
const { X, x } = xreact | |
function xmount(component, dom) { ReactDOM.render(React.createFactory(X)({}, component), dom) } | |
// Intent union type | |
interface Inc { | |
kind: 'inc' | |
} | |
interface Dec { | |
kind: 'dec' | |
} | |
type Intent = Inc | Dec | |
// State | |
interface CounterProps { | |
count: number | |
actions: Actions<Intent> | |
} | |
const CounterView: React.SFC<CounterProps> = ({actions, count}) => ( | |
<div> | |
<button onClick={actions.dec}>-</button> | |
<span>{count}</span> | |
<button onClick={actions.inc}>+</button> | |
</div> | |
) | |
CounterView.defaultProps = {count: 0} | |
const plan = (intent$) => { | |
return { | |
update$: intent$.map(intent => { | |
// now the switch is Type Safe | |
switch (intent.kind) { | |
case 'inc': | |
return state => ({ count: state.count + 1 }); | |
case 'dec': | |
return state => ({ count: state.count - 1 }); | |
default: | |
return _ => _; | |
} | |
}), | |
actions: { | |
inc: () => ({ kind: 'inc' } as Inc), | |
dec: () => ({ kind: 'dec' } as Dec) | |
} | |
} | |
} | |
const Counter = x(plan)(CounterView); | |
xmount(<Counter/>, | |
document.getElementById('app') | |
); |
import {render, html} from 'https://unpkg.com/uhtml?module'; | |
import {define, useState} from 'https://unpkg.com/hooked-elements?module'; | |
define('my-counter', element => { | |
const [count, update] = useState(0); | |
const {value, dec, inc} = render(element, html` | |
<button class="large btn" name="dec">-</button> | |
<span class="large value" name="value"></span> | |
<button class="large btn" name="inc">+</button> | |
`); | |
value.textContent = count; | |
dec.onclick = () => update(count - 1); | |
inc.onclick = () => update(count + 1); | |
}); |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Counter</title> | |
</head> | |
<body> | |
<script src="https://unpkg.com/apprun/dist/apprun-html.js"></script> | |
<script> | |
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(document.body, state, view, update); | |
</script> | |
</body> | |
</html> |
import React, { useDispatch } from 'reactn'; // <-- reactn | |
const incrementReducer = (global, dispatch, action) => ({ | |
count: global.count + action.amount, | |
}); | |
const decrementReducer = (global, dispatch, action) => ({ | |
count: global.count - action.amount, | |
}); | |
const MyComponent = () => { | |
const increment = useDispatch(incrementReducer); | |
const decrement = useDispatch(decrementReducer); | |
return ( | |
<div> | |
<button onClick={() => increment({ amount: 1 })}>Add 1</button> | |
<button onClick={() => increment({ amount: 3 })}>Add 3</button> | |
<button onClick={() => decrement({ amount: 5 })}>Subtract 5</button> | |
</div> | |
); | |
}; | |
export default MyComponent; |
import React from "react"; | |
import {createGlobalState} from 'react-use' | |
const useGlobalValue = createGlobalState(0); | |
const CompA = () => { | |
const [value, setValue] = useGlobalValue(); | |
return <button onClick={() => setValue(value + 1)}>+</button>; | |
}; | |
const CompB = () => { | |
const [value, setValue] = useGlobalValue(); | |
return <button onClick={() => setValue(value - 1)}>-</button>; | |
}; | |
export default function App() { | |
const [value] = useGlobalValue(); | |
return ( | |
<div> | |
<p>{value}</p> | |
<CompA /> | |
<CompB /> | |
</div> | |
); | |
} |
import { h, render, useState } from 'fre' | |
function App() { | |
const [count, setCount] = useState(0) | |
return ( | |
<div> | |
<h1>{count}</h1> | |
<button onClick={() => setCount(count + 1)}>+</button> | |
<button onClick={() => setCount(count + -)}>-</button> | |
</div> | |
) | |
} | |
render(<App />, document.getElementById('root')) |
import ReactDOM from "react" | |
import Concur from "concur" | |
const stateLoop = async function*(initState, component) { | |
let state = initState | |
while(true) { | |
state = yield* component(state) | |
} | |
} | |
const Counter = stateLoop(0, (count) => | |
<div> | |
<div>The current count is {count}</div> | |
<div> | |
<button onClick={mapProp(() => count+1)}>Increment</button> | |
<button onClick={mapProp(() => count-1)}>Decrement</button> | |
<button onClick={mapProp(() => 0)}>Reset</button> | |
</div> | |
</div> | |
) | |
ReactDOM.render(<Concur><Counter/></Concur>, document.getElementById('root')); |
import {neverland as $, render, html, useState} from 'neverland'; | |
const Counter = $((initialState) => { | |
const [count, setCount] = useState(initialState); | |
return html` | |
Count: ${count} | |
<button onclick=${() => setCount(count + 1)}>Increment</button> | |
<button onclick=${() => setCount(count - 1)}>Decrement</button> | |
}); | |
render(document.body, html` | |
<div> | |
${Counter(0)} | |
</div> | |
`); |
import { component, html, prop, useObservable } from 'js-elements' | |
component('simple-counter', { | |
props: { | |
label: prop.str.opt('Counter'), | |
initialCount: prop.num.opt(0) | |
} | |
}, props => { | |
const | |
state = useObservable({ count: props.initialCount }), | |
onIncrement = () => { ++state.count }, | |
onDecrement = () => { --state.count } | |
return () => html` | |
<div> | |
<label>${props.label}: </label> | |
<button @click=${onDecrement}>-</button> | |
<span> ${state.count} </span> | |
<button @click=${onIncrement}>+</button> | |
<div> | |
` | |
}) | |
document.getElementById('app').appendChild( | |
document.createElement('simple-counter')) |
import { h, component, render, useCallback, useState } from 'js-surface/react' | |
const Counter = component({ | |
name: 'Counter', | |
validate: true, | |
main({ initialValue = 0, label = 'Counter' }) { | |
const | |
[count, setCount] = useState(initialValue), | |
onIncrement = useCallback(() => setCount(it => it + 1), []) | |
return ( | |
<div> | |
<label>{label}</label> | |
<button onClick={onIncrement}>{count}</button> | |
</div> | |
) | |
} | |
}) | |
render(<Counter/>, 'app') |
class MyCounter extends HTMLElement { | |
constructor() { | |
super(); | |
this.count = 0; | |
const style = ` | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
`; | |
const html = ` | |
<button id="dec">-</button> | |
<span>${this.count}</span> | |
<button id="inc">+</button> | |
`; | |
this.attachShadow({ mode: 'open' }); | |
this.shadowRoot.innerHTML = ` | |
<style> | |
${style} | |
</style> | |
${html} | |
`; | |
this.buttonInc = this.shadowRoot.getElementById('inc'); | |
this.buttonDec = this.shadowRoot.getElementById('dec'); | |
this.spanValue = this.shadowRoot.querySelector('span'); | |
this.inc = this.inc.bind(this); | |
this.dec = this.dec.bind(this); | |
} | |
inc() { | |
this.count++; | |
this.update(); | |
} | |
dec() { | |
this.count--; | |
this.update(); | |
} | |
update() { | |
this.spanValue.innerText = this.count; | |
} | |
connectedCallback() { | |
this.buttonInc.addEventListener('click', this.inc); | |
this.buttonDec.addEventListener('click', this.dec); | |
} | |
disconnectedCallback() { | |
this.buttonInc.removeEventListener('click', this.inc); | |
this.buttonDec.removeEventListener('click', this.dec); | |
} | |
} | |
customElements.define('my-counter', MyCounter); |
import { html, render } from 'lighterhtml'; | |
class MyCounter extends HTMLElement { | |
constructor() { | |
super(); | |
this.count = 0; | |
this.attachShadow({ mode: 'open' }); | |
this.update(); | |
} | |
inc = () => { | |
this.count++; | |
this.update(); | |
}; | |
dec = () => { | |
this.count--; | |
this.update(); | |
}; | |
style() { | |
return ` | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
`; | |
} | |
template() { | |
return html` | |
<style> | |
${this.style()} | |
</style> | |
<button onclick="${this.dec}">-</button> | |
<span>${this.count}</span> | |
<button onclick="${this.inc}">+</button> | |
`; | |
} | |
update() { | |
render(this.shadowRoot, this.template()); | |
} | |
} | |
customElements.define('my-counter', MyCounter); |
import HyperHTMLElement from "hyperhtml-element"; | |
class MyCounter extends HyperHTMLElement { | |
constructor() { | |
super(); | |
this.attachShadow({ mode: "open" }); | |
} | |
created() { | |
this.count = 0; | |
this.render(); | |
} | |
inc = () => { | |
this.count++; | |
this.render(); | |
}; | |
dec = () => { | |
this.count--; | |
this.render(); | |
}; | |
render() { | |
return this.html` | |
<style> | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
</style> | |
<button onclick=${this.dec}>-</button> | |
<span>${this.count}</span> | |
<button onclick=${this.inc}>+</button> | |
`; | |
} | |
} | |
MyCounter.define("my-counter"); |
import { ComponentMixin } from "@neow/core"; | |
class MyComponent extends ComponentMixin(HTMLElement) { | |
static template = ` | |
<style> | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
</style> | |
<button onclick="{{this.dec()}}">-</button> | |
<span>{{this.counter}}</span> | |
<button onclick="{{this.inc()}}">+</button> | |
`; | |
counter = 0; | |
inc() { | |
this.counter++; | |
} | |
dec() { | |
this.counter--; | |
} | |
} | |
customElements.define("my-counter", MyComponent); |
import { define, WeElement, html } from "omi"; | |
class MyCounter extends WeElement { | |
static get propTypes() { | |
return { | |
count: Number | |
}; | |
} | |
static get defaultProps() { | |
return { count: 0 }; | |
} | |
install() { | |
this.data = { count: this.props.count }; | |
} | |
static get css() { | |
return ` | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
`; | |
} | |
inc = () => { | |
this.data.count++; | |
this.update(); | |
}; | |
dec = () => { | |
this.data.count--; | |
this.update(); | |
}; | |
render(props) { | |
return html` | |
<button onclick="${this.dec}">-</button> | |
<span>${this.data.count}</span> | |
<button onclick="${this.inc}">+</button> | |
`; | |
} | |
} | |
define("my-counter", MyCounter); |
import Element from "@skatejs/element"; | |
import { render, html } from "lit-html"; | |
class MyCounterElement extends Element { | |
static get props() { | |
return { | |
count: Number | |
}; | |
} | |
inc = () => { | |
this.count++; | |
}; | |
dec = () => { | |
this.count--; | |
}; | |
render() { | |
const style = ` | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
}`; | |
return html` | |
<style> | |
${style} | |
</style> | |
<button @click="${this.dec}"> | |
- | |
</button> | |
<span>${this.count}</span> | |
<button @click="${this.inc}"> | |
+ | |
</button> | |
`; | |
} | |
renderer() { | |
return render(this.render(), this.renderRoot); | |
} | |
} | |
customElements.define("my-counter", MyCounterElement); |
import Element, { h } from "@skatejs/element-preact"; | |
class MyCounterElement extends Element { | |
static get props() { | |
return { | |
count: Number | |
}; | |
} | |
inc = () => { | |
this.count++; | |
}; | |
dec = () => { | |
this.count--; | |
}; | |
render() { | |
const style = `host * { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
}`; | |
return ( | |
<host> | |
<style>{style}</style> | |
<button onclick={this.dec}>-</button> | |
<span>{this.count}</span> | |
<button onclick={this.inc}>+</button> | |
</host> | |
); | |
} | |
} | |
customElements.define("my-counter", MyCounterElement); |
import { Slim } from "slim-js/Slim.js"; | |
import { tag, template, useShadow } from "slim-js/Decorators"; | |
@tag("my-counter") | |
@useShadow(true) | |
@template(` | |
<style> | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
</style > | |
<button click="dec">-</button> | |
<span>{{parseCount(count)}}</span> | |
<button click="inc">+</button> | |
`) | |
class MyCounter extends Slim { | |
constructor() { | |
super(); | |
this.count = 0; | |
} | |
parseCount(num) { | |
return String(num); | |
} | |
inc() { | |
this.count++; | |
} | |
dec() { | |
this.count--; | |
} | |
} |
import { h, customElement, useProp } from "atomico"; | |
function MyCounter() { | |
let [count, setCount] = useProp("count"); | |
const style = ` | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
}`; | |
return ( | |
<host shadowDom> | |
<style>{style}</style> | |
<button onclick={() => setCount(count - 1)}>-</button> | |
<span>{count}</span> | |
<button onclick={() => setCount(count + 1)}>+</button> | |
</host> | |
); | |
} | |
MyCounter.props = { | |
count: { | |
type: Number, | |
reflect: true, | |
value: 0 | |
} | |
}; | |
customElements.define("my-counter", customElement(MyCounter)); |
import { define } from "heresy"; | |
define("MyCounter", { | |
style: MyCounter => ` | |
${MyCounter} * { | |
font-size: 200%; | |
} | |
${MyCounter} span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
${MyCounter} button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
`, | |
render({ useState }) { | |
const [count, update] = useState(0); | |
this.html` | |
<button onclick="${() => update(count - 1)}">-</button> | |
<span>${count}</span> | |
<button onclick="${() => update(count + 1)}">+</button> | |
`; | |
} | |
}); |
import Litedom from "litedom"; | |
Litedom({ | |
tagName: "my-counter", | |
shadowDOM: true, | |
template: ` | |
<button @click="dec">-</button> | |
<span>{this.count}</span> | |
<button @click="inc">+</button> | |
<style> | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
</style> | |
`, | |
data: { | |
count: 0 | |
}, | |
dec() { | |
this.data.count--; | |
}, | |
inc() { | |
this.data.count++; | |
} | |
}); |
import { component } from "ottavino"; | |
component({ | |
tag: "my-counter", | |
shadow: true, | |
template: ` | |
<style> | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
</style> | |
<button onclick="{{this.decrease()}}">-</button> | |
<span>{{this.count}}</span> | |
<button onclick="{{this.increase()}}" >+</button> | |
`, | |
properties: { | |
count: 0 | |
}, | |
this: { | |
increase: function() { | |
this.count++; | |
}, | |
decrease: function() { | |
this.count--; | |
} | |
} | |
}); |
import { LightningElement, api, buildCustomElementConstructor } from "lwc"; | |
export default class MyCounter extends LightningElement { | |
count = 0; | |
inc() { | |
this.count++; | |
} | |
dec() { | |
this.count--; | |
} | |
} | |
customElements.define("my-counter", buildCustomElementConstructor(MyCounter)); |
import { h, Component, State, Host } from "@stencil/core"; | |
@Component({ | |
tag: "my-counter", | |
styleUrl: "index.css", | |
shadow: true | |
}) | |
export class MyCounter { | |
@State() count: number = 0; | |
inc() { | |
this.count++; | |
} | |
dec() { | |
this.count--; | |
} | |
render() { | |
return ( | |
<Host> | |
<button onClick={this.dec.bind(this)}>-</button> | |
<span>{this.count}</span> | |
<button onClick={this.inc.bind(this)}>+</button> | |
</Host> | |
); | |
} | |
} |
import { createCustomElement } from "@wcd/preact-custom-element"; | |
import { Component, html } from "htm/preact"; | |
import "preact"; | |
class MyCounter extends Component { | |
state = { | |
count: 0 | |
}; | |
inc = () => { | |
this.setState(prev => ({ count: prev.count + 1 })); | |
}; | |
dec = () => { | |
this.setState(prev => ({ count: prev.count - 1 })); | |
}; | |
render(props, state) { | |
return html` | |
<style> | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
</style> | |
<button onClick=${this.dec}> | |
- | |
</button> | |
<span>${state.count}</span> | |
<button onClick=${this.inc}> | |
+ | |
</button> | |
`; | |
} | |
} | |
customElements.define("my-counter", createCustomElement(MyCounter, ["count"])); |
import React from "react"; | |
import ReactDOM from "react-dom"; | |
import reactToWebComponent from "react-to-webcomponent"; | |
interface State { | |
count: number; | |
} | |
interface Props {} | |
export default class MyCounter extends React.Component<Props, State> { | |
constructor(props) { | |
super(props); | |
this.state = { | |
count: 0 | |
}; | |
} | |
render() { | |
const styles = `.my-counter * { | |
font-size: 200%; | |
} | |
.my-counter span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
.my-counter button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
}`; | |
return ( | |
<div className="my-counter"> | |
<style>{styles}</style> | |
<button onClick={() => this.setState({ count: this.state.count - 1 })}> | |
- | |
</button> | |
<span>{this.state.count}</span> | |
<button onClick={() => this.setState({ count: this.state.count + 1 })}> | |
+ | |
</button> | |
</div> | |
); | |
} | |
} | |
customElements.define( | |
"my-counter", | |
reactToWebComponent(MyCounter, React, ReactDOM) | |
); |
import React, { useState } from "react"; | |
import ReactDOM from "react-dom"; | |
import reactToWebComponent from "react-to-webcomponent"; | |
export default function MyCounter() { | |
const [count, setCount] = useState(0); | |
const styles = ` | |
.my-counter * { | |
font-size: 200%; | |
} | |
.my-counter span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
.my-counter button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
}`; | |
return ( | |
<div className="my-counter"> | |
<style>{styles}</style> | |
<button onClick={() => setCount(count - 1)}>-</button> | |
<span>{count}</span> | |
<button onClick={() => setCount(count + 1)}>+</button> | |
</div> | |
); | |
} | |
customElements.define( | |
"my-counter", | |
reactToWebComponent(MyCounter, React, ReactDOM) | |
); |
<my-component> | |
<style> | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
</style> | |
<button onclick={dec}> | |
- | |
</button> | |
<span>{state.count}</span> | |
<button onclick={inc}> | |
+ | |
</button> | |
<script> | |
export default { | |
onBeforeMount(props, state) { | |
this.state = { | |
count: 0 | |
} | |
}, | |
inc() { | |
this.update({ | |
count: this.state.count+1 | |
}) | |
}, | |
dec() { | |
this.update({ | |
count: this.state.count-1 | |
}) | |
}, | |
} | |
</script> | |
</my-component> |
<svelte:options tag="my-counter" /> | |
<script> | |
let count = 0; | |
function inc() { | |
count++; | |
} | |
function dec() { | |
count--; | |
} | |
</script> | |
<style> | |
* { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
</style> | |
<button on:click={dec}> | |
- | |
</button> | |
<span>{count}</span> | |
<button on:click={inc}> | |
+ | |
</button> |
<template> | |
<div> | |
<button @click="this.dec">-</button> | |
<span>{{count}}</span> | |
<button @click="this.inc">+</button> | |
</div> | |
</template> | |
<script> | |
export default { | |
tag: 'my-counter', | |
name: 'MyCounter', | |
data() { | |
return { count: 0 } | |
}, | |
methods: { | |
inc: function() { | |
this.count++; | |
}, | |
dec: function() { | |
this.count--; | |
} | |
} | |
}; | |
</script> | |
<style scoped> | |
span, | |
button { | |
font-size: 200%; | |
} | |
span { | |
width: 4rem; | |
display: inline-block; | |
text-align: center; | |
} | |
button { | |
width: 64px; | |
height: 64px; | |
border: none; | |
border-radius: 10px; | |
background-color: seagreen; | |
color: white; | |
} | |
</style> |
import { render } from "preact"; | |
import flowponent from "flowponent"; | |
const App = flowponent(function*() { | |
let count = 0; | |
for (;;) { | |
count += yield resolve => ( | |
<div> | |
<div>current value: {count}</div> | |
<button onClick={() => resolve(1)}>+1</button> | |
<button onClick={() => resolve(-1)}>-1</button> | |
</div> | |
); | |
} | |
}); | |
render(<App />, document.getElementById("root")); |
import { useStoreon } from 'storeon-solidjs' | |
let counter = store => { | |
store.on('@init', () => ({ count: 0 })) | |
store.on('inc', ({ count }) => ({ count: count + 1 })) | |
store.on('dec', ({ count }) => ({ count: count - 1 })) | |
} | |
export default function Counter() { | |
const [state, dispatch] = useStoreon() | |
return ( | |
<div> | |
{state.count} | |
<button onClick={() => dispatch('inc')}>inc</button> | |
<button onClick={() => dispatch('dec')}>dec</button> | |
</div> | |
) | |
} |
module Examples.NoEffects.Counter.Main where | |
import Prelude | |
import Effect (Effect) | |
import Flame (QuerySelector(..), Html) | |
import Flame.Application.NoEffects as FAN | |
import Flame.HTML.Element as HE | |
import Flame.HTML.Attribute as HA | |
type Model = Int | |
data Message = Increment | Decrement | |
init :: Model | |
init = 0 | |
update :: Model -> Message -> Model | |
update model = case _ of | |
Increment -> model + 1 | |
Decrement -> model - 1 | |
view :: Model -> Html Message | |
view model = HE.main "main" [ | |
HE.button [HA.onClick Decrement] "-", | |
HE.text $ show model, | |
HE.button [HA.onClick Increment] "+" | |
] | |
main :: Effect Unit | |
main = FAN.mount_ (QuerySelector "main") { | |
init, | |
update, | |
view | |
} |
import { define } from 'uce'; | |
import { render, html } from 'uhtml'; | |
define("my-counter", { | |
init() { | |
this.count = 0; | |
this.dec = () => { | |
this.count--; | |
this.render(); | |
}; | |
this.inc = () => { | |
this.count++; | |
this.render(); | |
}; | |
this.render(); | |
}, | |
render() { | |
this.html` | |
<button onclick="${this.dec}">-</button> | |
<span>${this.count}</span> | |
<button onclick="${this.inc}">+</button> | |
`; | |
} | |
}); | |
addEventListener( | |
'DOMContentLoaded', | |
() => { | |
// populate the body | |
render(document.body, html` | |
<my-counter /> | |
<footer></footer> | |
`); | |
// fetch project details and populate the footer | |
fetch('package.json') | |
.then(_ => _.json()) | |
.then(({description, version}) => { | |
render(document.querySelector('footer'), html` | |
${description} v${version} | |
`); | |
}); | |
}, | |
{once: true} | |
); |
import React from "react"; | |
import { StateLake } from "statelake"; | |
const store = new StateLake({ count: ""}); | |
export default function App() { | |
const [count, setCount] = store.useState("count")(); | |
return ( | |
<div className="App"> | |
<button onClick={() => setCount(count + 1)}>+</button> | |
<button onClick={() => setCount(count - 1)}>-</button> | |
<div>{count}</div> | |
</div> | |
); | |
} |
open Feliz | |
open Feliz.UseElmish | |
open Elmish | |
type Msg = | |
| Increment | |
| Decrement | |
type State = { Count : int } | |
let init() = { Count = 0 }, Cmd.none | |
let update msg state = | |
match msg with | |
| Increment -> { state with Count = state.Count + 1 }, Cmd.none | |
| Decrement -> { state with Count = state.Count - 1 }, Cmd.none | |
let counter = React.functionComponent(fun () -> | |
let state, dispatch = React.useElmish(init, update, [| |]) | |
Html.div [ | |
Html.h1 state.Count | |
Html.button [ | |
prop.text "Increment" | |
prop.onClick (fun _ -> dispatch Increment) | |
] | |
Html.button [ | |
prop.text "Decrement" | |
prop.onClick (fun _ -> dispatch Decrement) | |
] | |
] | |
) |
import { wired, on, component } from "capsid"; | |
@component("counter") | |
export class Counter { | |
count: number = 0; | |
@wired(".label") | |
label: HTMLElement; | |
__mount__() { | |
this.update(); | |
} | |
@on.click.at(".plus") | |
plus() { | |
this.count++; | |
this.update(); | |
} | |
@on.click.at(".minus") | |
minus() { | |
this.count--; | |
this.update(); | |
} | |
/** Updates the label. */ | |
update() { | |
this.label.textContent = `${this.count}`; | |
} | |
} |
module Main where | |
import Prelude | |
import Effect (Effect) | |
import Concur.Core (Widget) | |
import Concur.React (HTML) | |
import Concur.React.DOM as D | |
import Concur.React.Props as P | |
import Concur.React.Run (runWidgetInDom) | |
counterWidget :: forall a. Int -> Widget HTML a | |
counterWidget count = do | |
n <- D.div' | |
[ D.p' [D.text ("State: " <> show count)] | |
, D.button [P.onClick] [D.text "Increment"] $> count+1 | |
, D.button [P.onClick] [D.text "Decrement"] $> count-1 | |
] | |
counterWidget n | |
main :: Effect Unit | |
main = runWidgetInDom "main" (counterWidget 0) |
import React from 'react'; | |
import ReactDOM from 'react-dom'; | |
import { Provider, createAtom, useAtom } from 'use-atom'; | |
const countAtom = createAtom({ default: 0 }); | |
const Counter = () => { | |
const [count, setCount] = useAtom(countAtom); | |
return ( | |
<div> | |
<span>Count: {count}</span> | |
<button type="button" onClick={() => setCount(count + 1)}>+1</button> | |
<button type="button" onClick={() => setCount((c) => c - 1)}>-1</button> | |
</div> | |
); | |
}; | |
const App = () => ( | |
<Provider> | |
<h1>Counter</h1> | |
<Counter /> | |
</Provider> | |
); |
import { createState } from "solid-js"; | |
const Counter = () => { | |
const [state, setState] = createState({ counter: 0 }); | |
return <div> | |
{state.counter} | |
<button type="button" onClick={() => setState({ counter: state.counter + 1 }}>+1</button> | |
<button type="button" onClick={() => setState({ counter: state.counter - 1 }}>-1</button> | |
</div>; | |
}; | |
render(() => createComponent(Counter, document.getElementById("app")); |
import React from 'react'; | |
import { hawk, hawkeye, useHawkState, useHawkSetState } from 'react-hawk'; | |
const counterState = hawk({ | |
key: 'counter', | |
default: 0 | |
}); | |
const isEvenState = hawkeye({ | |
key: 'isEven', | |
get: ({ get }) => { | |
const count = get(counterState) | |
return Boolean(count % 2) ? 'odd' : 'even' | |
} | |
}) | |
const useIncrease = () => { | |
const setCounter = useHawkSetState(counterState) | |
const increase = (n = 1) => { | |
setCounter(count => count + n) | |
} | |
return increase | |
} | |
const useDecrease = () => { | |
const setCounter = useHawkSetState(counterState) | |
const decrease = (n = 1) => { | |
setCounter(count => count - n) | |
} | |
return decrease | |
} | |
export const Counter = () => { | |
const count = useHawkState(counterState) | |
const even = useHawkState(isEvenState) | |
const decrease = useDecrease() | |
const increase = useIncrease() | |
return ( | |
<> | |
<button onClick={() => decrease()}>-</button> | |
{count} is {even} | |
<button onClick={() => increase()}>+</button> | |
</> | |
) | |
} |
const reducer = (state, action) => { | |
switch (action) { | |
case 'incr': return state + 1; | |
case 'decr': return state - 1; | |
} | |
}; | |
const Counter = () => { | |
const [count, dispatch] = useReducer(reducer, 0); | |
return x` | |
<div> | |
<p>Count: ${count}</p> | |
<button onclick=${() => dispatch('incr')}>Increment</button> | |
</div> | |
`; | |
}; |
import { h, text, app } from "https://unpkg.com/hyperapp" | |
import { main, h1, button } from "https://unpkg.com/@hyperapp/html" | |
const Subtract = (state) => ({ ...state, count: state.count - 1 }) | |
const Add = (state) => ({ ...state, count: state.count + 1 }) | |
app({ | |
init: (count = 0) => ({ count }), | |
view: (state) => | |
main({}, [ | |
h1({}, text(state.count)), | |
button({ onclick: Subtract }, text("-")), | |
button({ onclick: Add }, text("+")), | |
]), | |
node: document.getElementById("app"), | |
}) |
import React from 'react' | |
import create from 'xoid' | |
import { useAtom } from '@xoid/react' | |
const | |
$count = create(0), | |
increment = () => $count.value++, | |
decrement = () => $count.value--; | |
const CounterReact = () => { | |
const count = useAtom($count) | |
return ( | |
<> | |
<div>{count}</div> | |
<button onClick={increment}>+</button> | |
<button onClick={decrement}>-</button> | |
</> | |
) | |
} |
import { defineComponent } from 'vue' | |
import { useAtom } from '@xoid/vue' | |
const CounterVue = defineComponent(() => { | |
const count = useAtom($count) // same `$count` above | |
return () => ( | |
<> | |
<div>{count.value}</div> | |
<button onClick={increment}>+</button> | |
<button onClick={decrement}>-</button> | |
</> | |
) | |
}) |
This is awesome! One bug: There's a missing closing back-tick in 6-choo-choo-counter.js
(should be at end of line 21)
Count: <span id="count">0</span>
<button onclick="count.innerText = +count.innerText+1">+</button>
<button onclick="count.innerText = +count.innerText-1">-</button>
I hope the vanilla js one is tongue-in-cheek... because you'd have to go fishing in the DOM to get your data back, that'd become a real mess real quick in a real app.
Cool. As the developer of Calmm, I'd like to point out a couple of important details that are probably not immediately obvious just by looking at the code.
The first thing to understand is that the way state can be passed to components in Calmm via (lensed) atoms makes it possible to plug those components into application specific state fairly flexibly using lenses. The Counter
component only assumes that it gets access to a numeric variable, but that variable can be adapted to mean many things. The shopping cart example shows how the Counter
component can be adapted to control items in a shopping cart. In neither of the two uses of Counter
in that example is the state that the component manipulates just a number.
The second thing to understand is that the Counter
function that implements the component is only called by React when the component is mounted. In other words, the Counter
function is not called for each change of the counter value. In fact, when the counter value
changes, only the VDOM of the <div>Count: {value}</div>
element is recomputed and the corresponding DOM element is then updated. The way observables can be embedded into VDOM in Calmm means that VDOM updates can easily be made almost optimal.
Here is a slightly fixed version of the example:
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"))
I moved the construction of the state atom outside of the component to emphasize that the Counter
component can be a referentially transparent function. However, it is typical to define defaults, because that makes it easier to try out components, and it is also perfectly possible to create components that have local state that persists only for the duration that a component remains mounted.
@polytypic thanks for comments - updated
I was planning a second round with examples that are more involving - todo app with external API for persistence was original plan
but now shopping cart looks more like it 😄
Yes @foxdonut, definitely tongue-in-cheek :) It's also a parody of the succinctness that everyone is going for with their framework of choice. Without a defined set of objectives, Vanilla.js's example is just as valid as the others 😄
Great stuff @mindeavor :) totally agree!
In a twitter conversation someone mentioned this repo: https://github.com/adrianmcli/omg-counters
I wonder if this gist could me merged there
Act's <1.0 (https://github.com/act-framework/act/) version if you wanna add:
const view = (count) =>
['p', [
['h1', count],
['button', {click: {add: 1}}, '+'],
['button', {click: {add: -1}}, '-']
]]
const reducer = (state, {type, payload}) =>
type === 'add' ? state + payload : state
main(view, { model: 0, reducer })
Where does 1b-calmm-counter-elm
come from? As near as I can tell, https://github.com/polytypic/elm-reactive-dom-with-lensed-state-toy ? @polytypic
@adamdicarlo Yes, (I believe) it comes from there. Note that the Elm toy example is just a quick hack to show how things could look like. The Elm toy version is neither as expressive (it lacks higher-order observables and first-class atoms) nor as algorithmically efficient (updates require recomputing more VDOM) as the Calmm JS version, but there is no reason why one couldn't implement the approach properly in a ML-style language. Note that mentioning people in gist comments doesn't (seem to) give notifications.
@srdjan Hi 👋! Can we update the code in hyperapp's example? The part that says update
, should be actions
. That's it. 🙏
Pure React ?
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'));
How about Svelte?
<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>
Here's Marko:
class {
onCreate() {
this.state = { count: 0 };
}
increment(delta) {
this.state.count += delta;
}
}
<p>
${state.count}
<button on-click('increment', 1)>+</button>
<button on-click('increment', -1)>-</button>
</p>
To mount the Marko component:
require('./path/to/marko-counter').renderSync().appendTo(document.body)
DIO. v7.0.0
class Counter {
getInitialState () {
return {
count: 0
}
}
increment () {
return {
count: this.state.count + 1
}
}
decrement () {
return {
count: this.state.count - 1
}
}
render () {
return [
this.state.count,
h('button', {onClick: this.increment}, '+'),
h('button', {onClick: this.decrement}, '-')
]
}
}
dio.render(Counter);
How about Svelte?
There is a typo in the gist see https://gist.github.com/srdjan/1d10cbd42a2d695f696dee6b47fdc5e0#file-121-svetle-html
need to change sveTLe to sveLTe
and also it would be great to add svelte3 version
React with Effector
import React from "react";
import ReactDOM from "react-dom";
import { createStore, createEvent } from "effector";
import { createComponent } from "effector-react";
const increment = createEvent("increment");
const decrement = createEvent("decrement");
const resetCounter = createEvent("reset counter");
const counter = createStore(0)
.on(increment, state => state + 1)
.on(decrement, state => state - 1)
.reset(resetCounter);
const Counter = createComponent(counter, (props, counter) => (
<>
<div>{counter}</div>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={resetCounter}>reset</button>
</>
));
Effector + React hooks:
import React from "react";
import ReactDOM from "react-dom";
import { createStore, createEvent, createApi } from "effector";
import { useStore } from "effector-react";
const $counter = createStore(0)
const increment = createEvent();
const decrement = createEvent();
$counter.on(increment, state => state + 1)
$counter.on(decrement, state => state - 1)
function Counter () {
const counter = useStore($counter)
return (
<>
<div>{counter}</div>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</>
)
};
ReactDOM.render(<Counter />, document.body);
Count: <span id="count">0</span> <button onclick="count.innerText = +count.innerText+1">+</button> <button onclick="count.innerText = +count.innerText-1">-</button>
Gilbert That's awesome. The natural State of Light DOM is binding ID to a variable inside the window object. React guys won't tell you that. A similar mechanism is not available in the shadowRoot of Web Component. And that is great. I believe using getter and setter to IDs within web components can reproduce all the state management technique shown above. Just using getter and setter for defined IDs within a Shadow DOM, enter and exit strategy can be devised that can adopt any of above. Lets Discuss
Great stuff. I noticed something in 056-reausable.js. At line 21 there is a missing t letter from the component name. Also, 056-reausable.js has an extra a letter at the beginning.
thanks, fixed.
Awesome stuff. I noticed that, in 128-hooks-as-a-return-val.js and 133-fantasy-land.js, there should be square brackets around useState
calls.
xoid + React
import React from 'react'
import create from 'xoid'
import { useAtom } from '@xoid/react'
const
$count = create(0),
increment = () => $count.value++,
decrement = () => $count.value--;
const CounterReact = () => {
const count = useAtom($count)
return (
<>
<div>{count}</div>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</>
)
}
xoid + Vue
import { defineComponent } from 'vue'
import { useAtom } from '@xoid/vue'
const CounterVue = defineComponent(() => {
const count = useAtom($count) // same `$count` above
return () => (
<>
<div>{count.value}</div>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</>
)
})
thanks, I added xoid
examples
mithril
live demo