The tree was a debranded snapshot of upstream taken around 2026-02-20, and upstream has moved about five months since. This replaces it with upstream master, mechanically re-derived: bin/debrand does the rename, and the pipeline and squashed migrations are restored from the shipped tree. Not a merge. The previous import replaced the tree wholesale in a single commit, so there is no shared content history to merge against. Reproducing the rename on the new tree is what actually converges. What survives: .hanzo/workflows, the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md, and insights/migrations. The squashed initial migration is load-bearing — the live database records it by name, so upstream's ~918 individual migrations cannot come back. Nine products are dropped as unreferenced. Twenty-one more that we do not run are kept anyway because the core app still imports them; unpicking those is a refactor and does not belong in a rebase. This base does not build yet, and the reasons are not in the rename: upstream now wants Python 3.13.13 against our 3.12.12 and Django 5.2 against our 4.2, and the dependency manifests still name upstream packages that we republish under our own names. Those are decisions, not renames.
4.1 KiB
Connecting logics and React
Two parts to this: kea-to-kea (connect, imperative mounting) and kea-to-React
(useValues, useActions, BindLogic).
Pulling values and actions from another logic — connect
connect(() => ({
values: [teamLogic, ['currentTeamId', 'currentTeam'], userLogic, ['user']],
actions: [teamLogic, ['loadCurrentTeam']],
logic: [eventUsageLogic], // mount-only, no values pulled
}))
- Always use the function form
connect(() => ({...})). Defers evaluation past module load, so circular imports don't blow up. values: [logic, ['a', 'b'], otherLogic, ['c']]flattens — each logic followed by its names. The names become available onvaluesinside the logic.actions: [logic, ['x']]lets you both dispatchactions.x()and listen forxin your ownlisteners.logic: [otherLogic]mounts the logic without pulling anything. Use when you need the other logic running for its side effects (e.g. analytics, polling).
Connecting to a keyed logic
connect((props: FooLogicProps) => ({
values: [barLogic(props), ['something']],
}))
Take props and pass them through to the keyed logic. Same props → same key → same instance, so you talk to the same one as everyone else with the same props.
Mounting another logic from inside a logic
When you need a logic's instance (to track it, find it, or hand it to something),
use .build() + .mount():
listeners(({ actions, cache }) => ({
openInsight: ({ insightId }) => {
const logic = insightLogic.build({ dashboardItemId: insightId })
const unmount = logic.mount()
cache.disposables.add(() => unmount, `insightRef:${insightId}`)
},
}))
.build(props)returns the wrapper without mounting..mount()mounts it and returns an unmount function.- Register the unmount with
cache.disposablesso it's cleaned up automatically.
findMounted — peek without mounting
const logic = otherLogic.findMounted({ id })
if (logic) {
actions.doSomething(logic.values.x)
}
Returns undefined if the logic isn't mounted. Use when "if it happens to be
running, sync with it; otherwise nothing".
Wiring up React — useValues / useActions
import { useActions, useValues } from 'kea'
function FooView({ fooId }: { fooId: string }): JSX.Element {
const logic = fooLogic({ fooId }) // for keyed logics
const { foo, fooLoading } = useValues(logic)
const { setName } = useActions(logic)
return <input value={foo?.name ?? ''} onChange={(e) => setName(e.target.value)} />
}
For singletons, drop the (props): useValues(fooLogic).
These hooks are how a component subscribes to a logic. useValues re-renders the
component when the chosen values change; useActions returns stable action refs.
BindLogic — provide a keyed instance to a subtree
If a parent component knows the key and its children don't want to thread props
everywhere, wrap them in BindLogic:
<BindLogic logic={fooLogic} props={{ fooId }}>
<FooHeader /> {/* useValues(fooLogic) resolves to the bound instance */}
<FooDetails />
<FooFooter />
</BindLogic>
Inside the subtree, useValues(fooLogic) (no props) works because BindLogic set
the context.
Nest BindLogic when you have multiple keyed dependencies:
<BindLogic logic={dataNodeLogic} props={dataNodeProps}>
<BindLogic logic={dataVisualizationLogic} props={vizProps}>
<SQLEditor />
</BindLogic>
</BindLogic>
useMountedLogic — own the logic lifecycle
function FooHost(): JSX.Element {
useMountedLogic(fooLogic) // mounts on mount, unmounts on unmount
return <FooView />
}
Use when a logic should live exactly as long as a particular component, and no one
else is going to mount it. useValues and useActions already mount as a side
effect, so useMountedLogic is only needed when no values are pulled.
useAttachedLogic — keep a logic mounted through the scene
For scene-level child logics that must survive React unmounts, use useAttachedLogic.
Anti-patterns
See anti-patterns.md for the consolidated catalogue.