Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revisit Controlled and UnControlled Component #110

Open
reboottime opened this issue Nov 5, 2024 · 2 comments
Open

Revisit Controlled and UnControlled Component #110

reboottime opened this issue Nov 5, 2024 · 2 comments
Labels

Comments

@reboottime
Copy link
Owner

reboottime commented Nov 5, 2024

Controlled vs Uncontrolled component

Understand it from two levels:

  • React component vs DOM: state managed by react vs state managed by DOM. example: Input
  • State managed on the site of usage vs State managed by component itself. Example: Shadcn Dialog component
@reboottime
Copy link
Owner Author

Take Dialog component as an example

import React, { useState, useEffect } from 'react';

interface DialogProps {
  // For controlled usage
  open?: boolean;
  onOpenChange?: (open: boolean) => void;
  // For uncontrolled usage
  defaultOpen?: boolean;
  children?: React.ReactNode;
}

const Dialog = ({ 
  open: controlledOpen, 
  defaultOpen = false,
  onOpenChange,
  children 
}: DialogProps) => {
  // Internal state for uncontrolled mode
  const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
  
  // Determine if we're in controlled or uncontrolled mode
  const isControlled = controlledOpen !== undefined;
  
  // Use controlled value if provided, otherwise use internal state
  const isOpen = isControlled ? controlledOpen : uncontrolledOpen;

  // Handler for both controlled and uncontrolled modes
  const handleOpenChange = (newOpen: boolean) => {
    // In uncontrolled mode, update internal state
    if (!isControlled) {
      setUncontrolledOpen(newOpen);
    }
    
    // Always call onOpenChange if provided
    onOpenChange?.(newOpen);
  };

  // Update internal state if controlled prop changes
  useEffect(() => {
    if (isControlled) {
      setUncontrolledOpen(controlledOpen);
    }
  }, [isControlled, controlledOpen]);

  return (
    <>
      {/* Example implementation of dialog - simplified for clarity */}
      {isOpen && (
        <div className="fixed inset-0 bg-black/50">
          <div className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white p-6 rounded-lg">
            {/* Close button */}
            <button 
              onClick={() => handleOpenChange(false)}
              className="absolute top-2 right-2"
            >
              ×
            </button>
            
            {/* Dialog content */}
            {children}
          </div>
        </div>
      )}
    </>
  );
};

// DialogTrigger component to handle opening
interface DialogTriggerProps {
  asChild?: boolean;
  children: React.ReactNode;
}

const DialogTrigger = ({ asChild, children }: DialogTriggerProps) => {
  return (
    <button onClick={() => {
      // Find closest Dialog component and update its state
      // In real implementation, this would use React Context
      // to communicate with parent Dialog
    }}>
      {children}
    </button>
  );
};

// Example usage:
const Example = () => {
  // Uncontrolled usage
  return (
    <Dialog defaultOpen={false}>
      <DialogTrigger>Open Dialog</DialogTrigger>
    </Dialog>
  );

  // Controlled usage
  const [open, setOpen] = useState(false);
  return (
    <Dialog 
      open={open} 
      onOpenChange={setOpen}
    >
      <DialogTrigger>Open Dialog</DialogTrigger>
    </Dialog>
  );
};

@reboottime reboottime added the 2024 label Nov 5, 2024
@reboottime
Copy link
Owner Author

Understand controlled vs uncontrolled

function UncontrolledInput() {
  const inputRef = useRef();
  
  const handleSubmit = () => {
    console.log(inputRef.current.value);
  };

  return (
    <input
      ref={inputRef}
      defaultValue="initial value"
    />
  );
}

function ControlledInput() {
  const [value, setValue] = useState('');
  
  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant