Getting started

Kaiku is packaged to be easily used in both browser and as a module, no build tools required:

<script src="https://unpkg.com/kaiku"></script>
<script>
  const { h, render, createState } = kaiku
  const state = createState({ greeting: 'Hello world' })

  const App = () => h('span', null, state.greeting)

  render(h(App), document.body, state)
</script>

Or, just install the package using your favorite package manager:

# With NPM
npm i -s kaiku

# With yarn
yarn add kaiku

Usage with Babel

If you are already using a Babel preset such as @babel/preset-env, you have everything installed to make Kaiku work. The only thing you need to configure is how the JSX elements are transpiled into JavaScript by adding the following modification to your babel.config.json (or wherever your Babel configuration resides):

{
  "plugins": [["@babel/plugin-transform-react-jsx", { "pragma": "h" }]]
}

Usage with TypeScript

JSX with TypeScript is currently unsupported due to unfinished typings. The library itself is written strictly in TypeScript, but the way JSX is handled causes friction.

You can follow the progress on the main Github issue here.

Performance optimization

The global and mutable state management styles of Kaiku allow you to apply some optimizations not necessarily available in other libraries. In this section we will be going over some of them.

Remember to use the minified build in production

Kaiku ships with two distinct builds: kaiku.dev.js and kaiku.min.js. The former includes many debugging checks to catch common bugs, as well as the full name of each internal function. This is really helpful when developing the application, but the performance is significantly worse.

Once you are ready to ship your application, make sure to switch to the minified version to get the best performance out of your product.

Avoid arrow functions in the component body

Since Kaiku's global state management is made extremely simple, you can usually hoist the arrow functions you would normally require outside your function components. Consider the following simple case:

const state = createState({ inputValue: '' })

const InputComponent = () => {
  // Creates a function on every render, bad!
  const handleInput = (evt) => {
    state.inputValue = evt.target.value
  }

  // Since the identity of the `handleInput` function changes,
  // we also need to replace the previous handler every single time.
  return (
    <div>
      <input onInput={handleInput}>
      The current value reversed is
      <b>{state.inputValue.split('').reverse().join('')}</b>
    </div>
  )
}

Here our handleInput function is re-created every time the component is rendered. Since the state object can be mutated as you would an ordinary JavaScript object, we can move that function outside the component body:

// Single function for the entire lifetime of the application!
const handleInput = (evt) => {
  state.inputValue = evt.target.value
}

const InputComponent = () => {
  // The input handler doesn't get replaced either.
  return (
    <div>
      <input onInput={handleInput}>
      The current value reversed is
      <b>{state.inputValue.split('').reverse().join('')}</b>
    </div>
  )
}

Use the immutable utility function

Sometimes you need to load large, immutable chunks of data into your state. By default, Kaiku will process the state and starts tracking any potential changes to the object.

For example, say you have a multilingual website and you fetch and store the translations into the state. You rarely, if ever, want to touch the individual translation fields.

You can help Kaiku to know it does not need to keep track of changes to any of the individual keys by marking the state as immutable, using the immutable utility function:

import { createState, immutable } from 'kaiku'
import { fetchTranslations } from './api'

const state = createState({ translations: null })

function setLanguage(lang) {
  state.translations = immutable(await fetchTranslations(lang))
}

This causes Kaiku to not pay attention to any changes inside the state.translation object. This will both speed up the initial load of the translations and subsequent renders.

Working with mutable state

Passing state objects as props

const Person = ({ person }) => (
  <span>
    {person.name}
    <input
      type="checkbox"
      checked={person.checked}
      onClick={() => {
        // It's this easy!
        person.checked = !person.checked
      }}
    />
  </span>
)

export default function App() {
  const state = useState({
    people: [
      { name: 'Alice', checked: true },
      { name: 'Bob', checked: false },
      { name: 'Chris', checked: false },
      { name: 'David', checked: true },
    ],
  })

  const checkedPeople = state.people.filter((person) => person.checked)

  return (
    <div>
      <div>{checkedPeople.length} people checked in.</div>
      {state.people.map((person) => (
        <Person person={person} />
      ))}
    </div>
  )
}

Common pitfalls

Updating a primitive variable

With Kaiku's state management, you must always update a field of an object instead of copying the value and updating a secondary variable. Like with your common JavaScript objects, the following won't work:

const state = createState({ counter: 0 })
let { counter } = state
// This won't update the `state` object!
counter++

Why is this? Consider an ordinary object with a primitive value inside it, in our case a string:

const obj = { foo: 'bar' }

Since you cannot have references to primitive data types in JavaScript, the following makes a copy of the string instead of passing the reference. So, when we edit the variable, the object's field won't get updated:

let foo = obj.foo
foo = 'foobar'

console.log(obj.foo) // Outputs 'bar'
console.log(foo) // Outputs 'foobar'

Mutating objects not derived from the state object

const state = createState()

API reference

h

declare function h(
  component: string | FunctionComponent | Component,
  props: null | object,
  ...children: Children
): ElementDescriptor

render

declare function render(
  component: FunctionComponent | Component,
  root: HTMLElement,
  state?: State
): void

createState

declare function createState<T extends {}>(
  initialState: T
): State<T>

useState

declare function useState<T extends {}>(
  initialState: T
): State<T>

useRef

type Ref<T> = { current?: T }
declare function createState<T extends {}>(
  initialValue: T
): Ref<T>

Under the hood, the useRef implementation is just a small wrapper around the useState hook:

function useRef(initialValue) {
  return useState({ current: initialValue })
}

useEffect

declare function useEffect(
  effect: Function
): void

immutable

declare function immutable<T extends {}>(
  value: T
): Immutable<T>