본문으로 건너뛰기
cccskills
무료GitHub에 공개

abramov-state-composition

Write JavaScript code in the style of Dan Abramov, co-creator of Redux and React core team member. Emphasizes predictable state management, composition over inheritance, and developer experience. Use when building React applications or managing complex state.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md9.5 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Dan Abramov Style Guide

Overview

Dan Abramov is the co-creator of Redux, Create React App, and a member of the React core team. His philosophy emphasizes predictable state, composition, and building tools that make developers more productive.

Core Philosophy

"Redux is not the answer to all state management. It's one tool in the toolbox."

"The best code is the code that doesn't exist."

"Make impossible states impossible."

Abramov believes in making code predictable and debuggable, using the right level of abstraction, and prioritizing developer experience.

Design Principles

  1. Predictability: State changes should be predictable and traceable.

  2. Composition: Build complex from simple, not through inheritance.

  3. Explicit Over Magic: Prefer verbose clarity over clever brevity.

  4. Developer Experience: Tools should help developers, not fight them.

When Writing Code

Always

  • Keep state as flat as possible
  • Make state changes predictable and traceable
  • Use composition to build complex components
  • Colocate state with components that need it
  • Write components that are easy to test
  • Think about error boundaries

Never

  • Mutate state directly
  • Put everything in global state
  • Use inheritance for component reuse
  • Create deeply nested state structures
  • Ignore render performance in lists
  • Swallow errors silently

Prefer

  • Local state over global when possible
  • Hooks over class components
  • Function composition over inheritance
  • Explicit data flow over prop drilling solutions
  • Pure functions for state updates
  • Custom hooks for reusable logic

Code Patterns

Component Composition

// BAD: Prop drilling and inheritance thinking
function App() {
    return (
        <Layout 
            header={<Header user={user} onLogout={logout} />}
            sidebar={<Sidebar items={items} selected={selected} onSelect={select} />}
            content={<Content data={data} user={user} />}
        />
    );
}

// GOOD: Composition with children
function App() {
    return (
        <Layout>
            <Header>
                <UserMenu user={user} onLogout={logout} />
            </Header>
            <Sidebar>
                <Navigation items={items} selected={selected} onSelect={select} />
            </Sidebar>
            <Content>
                <Dashboard data={data} />
            </Content>
        </Layout>
    );
}


// Compound Components Pattern
function Tabs({ children, defaultIndex = 0 }) {
    const [activeIndex, setActiveIndex] = useState(defaultIndex);
    
    return (
        <TabsContext.Provider value={{ activeIndex, setActiveIndex }}>
            {children}
        </TabsContext.Provider>
    );
}

Tabs.List = function TabList({ children }) {
    return <div role="tablist">{children}</div>;
};

Tabs.Tab = function Tab({ index, children }) {
    const { activeIndex, setActiveIndex } = useContext(TabsContext);
    return (
        <button 
            role="tab"
            aria-selected={activeIndex === index}
            onClick={() => setActiveIndex(index)}
        >
            {children}
        </button>
    );
};

Tabs.Panels = function TabPanels({ children }) {
    const { activeIndex } = useContext(TabsContext);
    return Children.toArray(children)[activeIndex];
};

// Usage - composable and flexible
<Tabs defaultIndex={0}>
    <Tabs.List>
        <Tabs.Tab index={0}>First</Tabs.Tab>
        <Tabs.Tab index={1}>Second</Tabs.Tab>
    </Tabs.List>
    <Tabs.Panels>
        <Panel>First content</Panel>
        <Panel>Second content</Panel>
    </Tabs.Panels>
</Tabs>

Custom Hooks for Logic Reuse

// Extract reusable logic into custom hooks
function useLocalStorage(key, initialValue) {
    const [storedValue, setStoredValue] = useState(() => {
        try {
            const item = window.localStorage.getItem(key);
            return item ? JSON.parse(item) : initialValue;
        } catch (error) {
            console.error(error);
            return initialValue;
        }
    });

    const setValue = useCallback((value) => {
        try {
            const valueToStore = value instanceof Function 
                ? value(storedValue) 
                : value;
            setStoredValue(valueToStore);
            window.localStorage.setItem(key, JSON.stringify(valueToStore));
        } catch (error) {
            console.error(error);
        }
    }, [key, storedValue]);

    return [storedValue, setValue];
}


// Async data fetching hook
function useAsync(asyncFunction, immediate = true) {
    const [status, setStatus] = useState('idle');
    const [value, setValue] = useState(null);
    const [error, setError] = useState(null);

    const execute = useCallback(async () => {
        setStatus('pending');
        setValue(null);
        setError(null);
        
        try {
            const response = await asyncFunction();
            setValue(response);
            setStatus('success');
        } catch (error) {
            setError(error);
            setStatus('error');
        }
    }, [asyncFunction]);

    useEffect(() => {
        if (immediate) {
            execute();
        }
    }, [execute, immediate]);

    return { execute, status, value, error };
}

State Management Patterns

// Pattern 1: Colocate state
// State should live as close to where it's used as possible

// BAD: Lifting state too high
function App() {
    const [searchQuery, setSearchQuery] = useState('');
    const [results, setResults] = useState([]);
    // ... passed down through many layers
}

// GOOD: State lives where it's used
function SearchComponent() {
    const [searchQuery, setSearchQuery] = useState('');
    const [results, setResults] = useState([]);
    // Only this component cares about search
}


// Pattern 2: Reducer for complex state
function reducer(state, action) {
    switch (action.type) {
        case 'FETCH_START':
            return { ...state, loading: true, error: null };
        case 'FETCH_SUCCESS':
            return { ...state, loading: false, data: action.payload };
        case 'FETCH_ERROR':
            return { ...state, loading: false, error: action.payload };
        default:
            throw new Error(`Unknown action: ${action.type}`);
    }
}

function DataComponent() {
    const [state, dispatch] = useReducer(reducer, {
        data: null,
        loading: false,
        error: null
    });
    
    // Actions are explicit and traceable
    const fetchData = async () => {
        dispatch({ type: 'FETCH_START' });
        try {
            const data = await api.getData();
            dispatch({ type: 'FETCH_SUCCESS', payload: data });
        } catch (error) {
            dispatch({ type: 'FETCH_ERROR', payload: error.message });
        }
    };
}


// Pattern 3: Make impossible states impossible
// BAD: Multiple booleans that can conflict
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
// What if isLoading AND isError are both true?

// GOOD: Single status that can only be one thing
const [status, setStatus] = useState('idle'); // 'idle' | 'loading' | 'error' | 'success'

Performance Patterns

// Memoize expensive computations
const expensiveValue = useMemo(() => {
    return computeExpensiveValue(a, b);
}, [a, b]);

// Memoize callbacks passed to children
const handleClick = useCallback((id) => {
    setSelected(id);
}, []);

// Memoize components that receive stable props
const MemoizedChild = React.memo(function Child({ data, onClick }) {
    return <div onClick={onClick}>{data.name}</div>;
});

// Don't over-optimize! Profile first
// BAD: Premature optimization everywhere
const value = useMemo(() => a + b, [a, b]);  // Simple addition doesn't need memo

// GOOD: Optimize what matters
// - Large lists with React.memo on items
// - Expensive computations with useMemo
// - Context values to prevent cascading rerenders

Error Boundaries

class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false, error: null };
    }

    static getDerivedStateFromError(error) {
        return { hasError: true, error };
    }

    componentDidCatch(error, errorInfo) {
        console.error('Error caught by boundary:', error, errorInfo);
        // Log to error reporting service
    }

    render() {
        if (this.state.hasError) {
            return this.props.fallback || <h1>Something went wrong.</h1>;
        }
        return this.props.children;
    }
}

// Usage: wrap parts of your app
<ErrorBoundary fallback={<ErrorPage />}>
    <FeatureComponent />
</ErrorBoundary>

Mental Model

Abramov approaches React code by asking:

  1. Where should this state live? As low as possible, as high as necessary
  2. Is this predictable? Can I trace how we got here?
  3. Can this be composed? Small pieces that combine well
  4. Is this testable? Pure functions, clear inputs/outputs
  5. What can go wrong? Error boundaries, loading states

Signature Abramov Moves

  • Composition over inheritance, always
  • Custom hooks for reusable logic
  • useReducer for complex state transitions
  • Make impossible states impossible
  • Colocate state near usage
  • Memoize strategically, not everywhere

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Master backend architectures, REST API design, microservices, and server-side development across multiple languages. Use this as a central index to access specialized sub-skills.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MikeCheng1208/BattleTree22026년 7월 22일 업데이트

在每次回复中使用固定前缀并列出本次实际参考的指令/文件,便于校验是否遵循上下文与规则。

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MikeCheng1208/BattleTree22026년 7월 22일 업데이트

Automate 21risk tasks via Rube MCP (Composio). Always search tools first for current schemas.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MikeCheng1208/BattleTree22026년 7월 22일 업데이트

Automate 2chat tasks via Rube MCP (Composio). Always search tools first for current schemas.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MikeCheng1208/BattleTree22026년 7월 22일 업데이트

Arquitecto de Soluciones Principal y Consultor Tecnológico de Andru.ia. Diagnostica y traza la hoja de ruta óptima para proyectos de IA en español.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MikeCheng1208/BattleTree22026년 7월 22일 업데이트

[00] META. Orchestrate skills into dynamic chains for complex tasks. Analyzes the task, discovers available skills, builds an optimal chain, explains why each skill is needed, and executes step-by-step with user confirmation. Use for any complex task requiring multiple thinking/research/analysis steps. Triggers on "plan this", "how to approach", "what's the strategy", "build a plan", or any multi-step problem requiring skill orchestration.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MikeCheng1208/BattleTree22026년 7월 22일 업데이트

MikeCheng1208의 스킬 모두 보기

이 스킬의 문제 신고