fix(webapp): call hooks directly and unconditionally (#4715)

## Summary

Calls dashboard hooks directly instead of passing them as ordinary
callback values, and subscribes to optional Ariakit stores through an
unconditional hook. This keeps hook ordering stable while preserving the
existing behavior when a provider is absent.
This commit is contained in:
Chris Arderne
2026-08-20 09:59:39 +01:00
committed by GitHub
parent 6dfc54b75b
commit 176fb6daf4
5 changed files with 11 additions and 8 deletions
@@ -1,6 +1,7 @@
import * as Ariakit from "@ariakit/react";
import { type SelectProps as AriaSelectProps } from "@ariakit/react";
import { SelectValue } from "@ariakit/react-core/select/select-value";
import { useStoreState } from "@ariakit/react-core/utils/store";
import { Link } from "@remix-run/react";
import * as React from "react";
import { Fragment, useMemo, useState } from "react";
@@ -484,7 +485,7 @@ export function SelectItem({
const render = combobox ? <Ariakit.ComboboxItem render={props.render} /> : props.render;
const ref = React.useRef<HTMLDivElement>(null);
const select = Ariakit.useSelectContext();
const selectValue = select?.useState("value");
const selectValue = useStoreState(select, "value");
const isChecked = React.useMemo(() => {
if (!props.value || selectValue == null) return false;
@@ -692,8 +693,8 @@ export function ComboBox({
...props
}: ComboBoxProps) {
const combobox = Ariakit.useComboboxContext();
const open = combobox?.useState("open");
const input = combobox?.useState("baseElement");
const open = useStoreState(combobox, "open");
const input = useStoreState(combobox, "baseElement");
React.useEffect(() => {
if (!open || !input) return;
+1 -2
View File
@@ -2,7 +2,7 @@ import { useEffect, useRef } from "react";
/** Call a function when the id of the item changes */
export function useChanged<T extends { id: string }>(
getItem: () => T | undefined,
item: T | undefined,
action: (item: T | undefined) => void,
sendInitialUndefined = true
) {
@@ -10,7 +10,6 @@ export function useChanged<T extends { id: string }>(
const isInitialRender = useRef(true);
const actionRef = useRef(action);
const itemRef = useRef<T | undefined>();
const item = getItem();
const itemId = item?.id;
actionRef.current = action;
+2 -1
View File
@@ -43,7 +43,8 @@ export function useOrganization(matches?: UIMatch[]) {
}
export const useOrganizationChanged = (action: (org: MatchedOrganization | undefined) => void) => {
useChanged(useOptionalOrganization, action);
const organization = useOptionalOrganization();
useChanged(organization, action);
};
export function useIsImpersonating(matches?: UIMatch[]) {
+2 -1
View File
@@ -24,5 +24,6 @@ export function useProject(matches?: UIMatch[]) {
}
export const useProjectChanged = (action: (org: MatchedProject | undefined) => void) => {
useChanged(useOptionalProject, action);
const project = useOptionalProject();
useChanged(project, action);
};
+2 -1
View File
@@ -27,7 +27,8 @@ export function useUser(matches?: UIMatch[]): User {
}
export function useUserChanged(callback: (user: User | undefined) => void) {
useChanged(useOptionalUser, callback);
const user = useOptionalUser();
useChanged(user, callback);
}
/**