Tích hợp Leva với Redux, Zustand và Recoil để quản lý trạng thái hiệu quả trong React

Leva là một thư viện GUI dành riêng cho React, cung cấp khả năng quản lý trạng thái mạnh mẽ cùng các hook an toàn về kiểu dữ liệu — mà không bắt buộc phải render giao diện mặc định. Bài viết này hướng dẫn cách tích hợp Leva với ba thư viện quản lý trạng thái phổ biến: Redux, Zustand và Recoil.

Kết nối Leva với Zustand

Vì Leva sử dụng Zustand làm nền tảng bên trong, việc tích hợp giữa hai thư viện này rất liền mạch.

Truy cập trực tiếp store của Zustand từ Leva

import { levaStore } from 'leva/headless';

// Lấy toàn bộ dữ liệu
const allData = levaStore.getData();

// Lấy giá trị cụ thể theo đường dẫn
const currentCount = levaStore.get('uiSettings.counter');

// Cập nhật giá trị
levaStore.set({ 'uiSettings.counter': 10 }, false);

// Theo dõi thay đổi
const unsubscribe = levaStore.useStore.subscribe(
  (state) => state.data['uiSettings.counter'],
  (newValue) => console.log('Giá trị mới:', newValue)
);

Tạo store tùy chỉnh với useCreateStore

const customStore = useCreateStore();
const configValues = useControls({ speed: 5 }, { store: customStore });
const inputList = useLevaInputs(customStore);

Cách này cho phép bạn duy trì nhiều store Leva độc lập, phù hợp với kiến trúc ứng dụng phức tạp.

Kết hợp Leva và Redux

Mặc dù không có hỗ trợ chính thức, bạn vẫn có thể đồng bộ Leva với Redux thông qua middleware hoặc action creators.

Middleware Redux để lắng nghe thay đổi từ Leva

const syncLevaToRedux = (reduxStore) => (next) => (action) => {
  const cleanup = levaStore.useStore.subscribe(
    (state) => state.data,
    (data) => reduxStore.dispatch({ type: 'SYNC_LEVA_STATE', payload: data })
  );

  const result = next(action);
  return () => {
    cleanup();
    return result;
  };
};

Cập nhật Leva từ Redux action

const setLevaValue = (keyPath, value) => (dispatch) => {
  levaStore.set({ [keyPath]: value }, false);
  dispatch({ type: 'UPDATE_FROM_LEVA', payload: { keyPath, value } });
};

Đồng bộ Leva với Recoil

Recoil quản lý trạng thái dưới dạng "atom", do đó cần tạo hook tùy chỉnh để liên kết hai hệ thống.

Hook đồng bộ hai chiều

import { useEffect } from 'react';
import { useRecoilState } from 'recoil';
import { useLevaInput } from 'leva/headless';

function useSyncRecoilLeva(recoilAtom, levaKey) {
  const [recoilVal, setRecoil] = useRecoilState(recoilAtom);
  const { value: levaVal, set: setLeva } = useLevaInput(levaKey);

  useEffect(() => {
    if (recoilVal !== levaVal) setLeva(recoilVal);
  }, [recoilVal, setLeva, levaVal]);

  useEffect(() => {
    if (levaVal !== recoilVal) setRecoil(levaVal);
  }, [levaVal, setRecoil, recoilVal]);

  return [recoilVal, setRecoil];
}

Sử dụng trong component

const themeAtom = atom({ key: 'themeColor', default: '#ffffff' });

function ThemeEditor() {
  const [color] = useSyncRecoilLeva(themeAtom, 'appearance.color');
  return <div>Màu hiện tại: {color}</div>;
}

Chế độ Headless – Nền tảng cho mọi tích hợp

Chế độ headless của Leva cung cấp các API chính sau:

  • useControls: Quản lý trạng thái mà không render UI
  • useLevaInputs: Truy xuất danh sách input và metadata
  • useLevaTree: Lấy cấu trúc cây thư mục của control
  • useLevaInput: Truy cập input theo đường dẫn
  • useCreateStore: Tạo store độc lập

Ứng dụng thực tế

Tích hợp với React Three Fiber

function AnimatedCube() {
  const { scale, rotationSpeed, materialColor } = useControls({
    scale: { value: 1, min: 0.1, max: 3 },
    rotationSpeed: { value: 0.02, min: 0, max: 0.1 },
    materialColor: '#00ffcc'
  });

  useFrame((_, delta) => {
    // Áp dụng giá trị từ Leva vào animation 3D
  });

  return (
    <mesh scale={scale}>
      <boxGeometry />
      <meshStandardMaterial color={materialColor} />
    </mesh>
  );
}

Giao diện điều khiển trong WebXR

function XRPanel() {
  useControls({
    movementSpeed: { value: 2, min: 0.5, max: 5 },
    enableGravity: true,
    skyColor: '#87CEEB'
  });

  const inputs = useLevaInputs();
  return (
    <group position={[0, 1.6, -0.8]}>
      {inputs.map(({ path, input }) => (
        <XRButton key={path} label={input.label} valuePath={path} />
      ))}
    </group>
  );
}

Thẻ: Leva Zustand redux Recoil react

Đăng vào ngày 5 tháng 8 lúc 17:22