Développement de composants Component

Dans NocoBase, les composants de page montés sur des routes sont de simples composants React. Vous pouvez les écrire directement avec React + Antd, comme en développement front-end ordinaire.

NocoBase fournit en plus :

  • observable + observer — la gestion d'état recommandée, plus adaptée à l'écosystème NocoBase que useState
  • useFlowContext() — récupère les capacités du contexte NocoBase (envoi de requêtes, internationalisation, navigation entre routes, etc.)

Écriture de base

Un composant de page minimal :

// pages/HelloPage.tsx
export default function HelloPage() {
  return <h1>Hello, NocoBase!</h1>;
}

Une fois écrit, enregistrez-le avec this.router.add() dans le load() du plugin. Voir Router pour les détails.

Gestion d'état : observable

NocoBase recommande observable + observer pour gérer l'état des composants plutôt que useState. Avantages :

  • Modifier directement les propriétés de l'objet déclenche la mise à jour, sans setState
  • Collection automatique des dépendances : le composant ne re-rend que lorsque les propriétés utilisées changent
  • Cohérent avec le mécanisme réactif de la couche basse de NocoBase (FlowModel, FlowContext, etc.)

Utilisation de base : créez un objet réactif avec observable.deep() et enveloppez le composant avec observer(). observable et observer s'importent depuis @nocobase/flow-engine :

import React from 'react';
import { Input } from 'antd';
import { observable, observer } from '@nocobase/flow-engine';

// Crée un objet d'état réactif
const state = observable.deep({
  text: '',
});

// Enveloppe le composant avec observer pour mise à jour automatique
const DemoPage = observer(() => {
  return (
    <div>
      <Input
        placeholder="Saisissez quelque chose..."
        value={state.text}
        onChange={(e) => {
          state.text = e.target.value;
        }}
      />
      {state.text && <div style={{ marginTop: 8 }}>Vous avez saisi : {state.text}</div>}
    </div>
  );
});

export default DemoPage;

Aperçu :

import { Input } from 'antd';
import { observable, observer } from '@nocobase/flow-engine';

// Crée un objet d'état réactif
const state = observable.deep({
  text: '',
});

// Enveloppez le composant avec observer pour qu'il se mette à jour automatiquement lors des changements d'état
const DemoPage = observer(() => {
  return (
    <div>
      <Input
        placeholder="Saisissez quelque chose..."
        value={state.text}
        onChange={(e) => {
          state.text = e.target.value;
        }}
      />
      {state.text && <div style={{ marginTop: 8 }}>Vous avez saisi : {state.text}</div>}
    </div>
  );
});

export default DemoPage;

Voir Mécanisme réactif Observable pour plus de détails.

Utilisation de useFlowContext

useFlowContext() est le point d'entrée pour accéder aux capacités de NocoBase. Importez-le depuis @nocobase/flow-engine ; il renvoie un objet ctx :

import { useFlowContext } from '@nocobase/flow-engine';

export default function MyPage() {
  const ctx = useFlowContext();
  // ctx.api — envoyer des requêtes
  // ctx.t — internationalisation
  // ctx.router — navigation entre routes
  // ctx.logger — logs
  // ...
}

Voici quelques exemples d'utilisations courantes.

Envoyer une requête

ctx.api.request() appelle les API back-end, avec la même utilisation qu'Axios :

const response = await ctx.api.request({
  url: 'users:list',
  method: 'get',
});
console.log(response.data);

Internationalisation

ctx.t() renvoie le texte traduit :

const label = ctx.t('Hello');
// Spécifier le namespace
const msg = ctx.t('Save success', { ns: '@my-project/plugin-hello' });

ctx.router.navigate() permet de naviguer vers une autre page :

ctx.router.navigate('/some-page'); // -> /v/some-page

Récupérer les paramètres de la route courante :

// par exemple route définie comme /users/:id
const { id } = ctx.route.params; // récupère le paramètre dynamique

Récupérer le nom de la route courante :

const { name } = ctx.route; // récupère le nom de la route

Voir Context → Capacités courantes pour plus de niveaux et d'utilisations de logs.

Exemple complet

En combinant observable, useFlowContext et Antd, voici un composant de page qui récupère et affiche des données du back-end :

// pages/PostListPage.tsx
import React, { useEffect } from 'react';
import { Button, Card, List, Spin } from 'antd';
import { observable, observer, FlowContext, useFlowContext } from '@nocobase/flow-engine';

interface Post {
  id: number;
  title: string;
}

// État de la page géré par observable
const state = observable.deep({
  posts: [] as Post[],
  loading: true,
});

const PostListPage = observer(() => {
  const ctx = useFlowContext();

  useEffect(() => {
    loadPosts(ctx);
  }, []);

  return (
    <Card title={ctx.t('Post list')}>
      <Spin spinning={state.loading}>
        <List
          dataSource={state.posts}
          renderItem={(post: Post) => (
            <List.Item
              actions={[
                <Button danger onClick={() => handleDelete(ctx, post.id)}>
                  {ctx.t('Delete')}
                </Button>,
              ]}
            >
              {post.title}
            </List.Item>
          )}
        />
      </Spin>
    </Card>
  );
});

async function loadPosts(ctx: FlowContext) {
  state.loading = true;
  try {
    const response = await ctx.api.request({
      url: 'posts:list',
      method: 'get',
    });
    state.posts = response.data?.data || [];
  } catch (error) {
    ctx.logger.error('Échec du chargement de la liste des articles', { error });
  } finally {
    state.loading = false;
  }
}

async function handleDelete(ctx: FlowContext, id: number) {
  await ctx.api.request({
    url: `posts:destroy/${id}`,
    method: 'post',
  });
  loadPosts(ctx); // rafraîchit la liste
}

export default PostListPage;

Et ensuite ?

  • Capacités complètes fournies par useFlowContext — voir Context
  • Styles et personnalisation du thème — voir Styles & Thèmes
  • Si votre composant doit apparaître dans le menu « Ajouter un bloc / champ / action » et supporter la configuration visuelle, il faut l'encapsuler avec FlowModel — voir FlowEngine
  • Vous hésitez entre Component et FlowModel ? — voir Component vs FlowModel

Liens connexes