### Install React Tracked Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/quick-start.md Command to install the react-tracked library using npm or yarn. ```bash npm install react-tracked # Or yarn add react-tracked ``` -------------------------------- ### Running Examples (Bash) Source: https://github.com/dai-shi/react-tracked/blob/main/README.md Provides instructions on how to run the examples included in the react-tracked project using pnpm. It specifies the command to set the port and start the minimal example. ```bash PORT=8080 pnpm run examples:01_minimal ``` -------------------------------- ### Run React App Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/quick-start.md Commands to navigate into the created application directory and start the development server. ```bash cd my-app npm start # Or yarn start ``` -------------------------------- ### Download and Run To Do List Example (Bash) Source: https://github.com/dai-shi/react-tracked/blob/main/examples/07_todolist/README.md This snippet demonstrates how to download the example code using curl and then run it using yarn. It assumes a bash environment. ```bash curl https://codeload.github.com/dai-shi/react-tracked/tar.gz/main | tar -xz --strip=2 react-tracked-main/examples/07_todolist cd 07_todolist yarn && yarn run start ``` -------------------------------- ### React Global State with React Tracked Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/quick-start.md Replaces the bare React Context implementation with `createContainer` from react-tracked. This setup provides the same functionality but with improved performance. ```typescript import { useState } from 'react'; import { createContainer } from 'react-tracked'; const initialState = { count: 0, text: 'hello', }; const useMyState = () => useState(initialState); export const { Provider: SharedStateProvider, useTracked: useSharedState } = createContainer(useMyState); ``` -------------------------------- ### Create React App Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/quick-start.md Command to create a new React application using create-react-app. Supports TypeScript with the `--template typescript` flag. ```bash npx create-react-app my-app # Add --template typescript for TypeScript ``` -------------------------------- ### Install React Tracked Source: https://github.com/dai-shi/react-tracked/blob/main/README.md Installs the react-tracked package and its peer dependency 'react'. This is the initial step to integrate state usage tracking into your React application. ```bash npm install react-tracked react scheduler ``` -------------------------------- ### Root Component Setup (App.js) Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-03.md The root component of the ToDo application. It sets up the React-Tracked Provider to manage the application's state and wraps the main TodoList component. ```typescript import React from 'react'; import { Provider } from '../store'; import TodoList from './TodoList'; const App = () => ( ); export default App; ``` -------------------------------- ### React App Component with Context Provider Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/quick-start.md The main App component that wraps the Counter and TextBox components with the `SharedStateProvider` to make the global state available. ```typescript import React from 'react'; import logo from './logo.svg'; import './App.css'; import { SharedStateProvider } from './store'; import Counter from './Counter'; import TextBox from './TextBox'; const App = () => (
logo
); export default App; ``` -------------------------------- ### React-Tracked Store Setup with useState Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-01.md This snippet shows how to create a store using `react-tracked`'s `createContainer` and `useState`. It defines the state shape and exports the `Provider`, `useTrackedState`, and a renamed `useUpdate` hook as `useSetState`. ```typescript import { useState } from 'react'; import { createContainer } from 'react-tracked'; export type State = { firstName?: string; lastName?: string; }; const useValue = () => useState({}); export const { Provider, useTrackedState, useUpdate: useSetState, } = createContainer(useValue); ``` -------------------------------- ### Create Container with useReducer Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/debugging.md Example of creating a container using `useReducer` for state management in React Tracked. This sets up the Provider and `useTracked` hook. ```javascript const useValue = () => useReducer(reducer, initialState); export const { Provider, useTracked } = createContainer(useValue); ``` -------------------------------- ### React Zustand: Person Name Management with useStore Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-zustand-01.md This snippet demonstrates managing person's first and last names using zustand's `useStore` hook in a React application. It includes components for editing names and displaying them, with an option to show only the first name. The example highlights the use of selectors for accessing and updating state. ```typescript import * as React from "react"; import { useState } from "react"; import create from "zustand"; type State = { firstName: string; lastName: string; setFirstName: (firstName: string) => void; setLastName: (lastName: string) => void; }; const useStore = create((set) => ({ firstName: "React", lastName: "Tracked", setFirstName: (firstName) => set({ firstName }), setLastName: (lastName) => set({ lastName }) })); const EditPerson = () => { const firstName = useStore((state) => state.firstName); const lastName = useStore((state) => state.lastName); const setFirstName = useStore((state) => state.setFirstName); const setLastName = useStore((state) => state.setLastName); return (
First Name: setFirstName(e.target.value)} />
Last Name: setLastName(e.target.value)} />
); }; const ShowPerson = () => { const [onlyFirstName, setOnlyFirstName] = useState(false); const firstName = useStore((state) => state.firstName); const lastName = useStore((state) => (onlyFirstName ? null : state.lastName)); return (
{onlyFirstName ? (
First Name: {firstName}
) : (
Full Name: {firstName} {lastName}
)}
); }; const App = () => { return ( <> ); }; export default App; ``` -------------------------------- ### React Global State with Context Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/quick-start.md Defines a global state using React's Context API. It includes a state hook (`useMyState`), a context provider (`SharedStateProvider`), and a custom hook (`useSharedState`) to consume the state. ```typescript import React, { ReactNode, createContext, useState, useContext } from 'react'; const initialState = { count: 0, text: 'hello', }; const useMyState = () => useState(initialState); const MyContext = createContext | null>(null); export const useSharedState = () => { const value = useContext(MyContext); if (value === null) throw new Error('Please add SharedStateProvider'); return value; }; export const SharedStateProvider = ({ children }: { children: ReactNode }) => ( {children} ); ``` -------------------------------- ### React Zustand: Person Name Management with useTrackedStore Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-zustand-01.md This snippet demonstrates an alternative approach to managing person's first and last names in React using zustand's `createTrackedSelector`. It simplifies state selection compared to the standard `useStore` hook, making conditional selectors more straightforward. The example includes components for editing and displaying names. ```typescript import * as React from "react"; import { useState } from "react"; import create from "zustand"; import { createTrackedSelector } from "react-tracked"; type State = { firstName: string; lastName: string; setFirstName: (firstName: string) => void; setLastName: (lastName: string) => void; }; const useStore = create((set) => ({ firstName: "React", lastName: "Tracked", setFirstName: (firstName) => set({ firstName }), setLastName: (lastName) => set({ lastName }) })); const useTrackedStore = createTrackedSelector(useStore); const EditPerson = () => { const state = useTrackedStore(); return (
First Name: state.setFirstName(e.target.value)} />
Last Name: state.setLastName(e.target.value)} />
); }; const ShowPerson = () => { const state = useTrackedStore(); const [onlyFirstName, setOnlyFirstName] = useState(false); return (
{onlyFirstName ? (
First Name: {state.firstName}
) : (
Full Name: {state.firstName} {state.lastName}
)}
); }; const App = () => { return ( <> ); }; export default App; ``` -------------------------------- ### React Counter Component Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/quick-start.md A React component that displays a counter and a button to increment it. It uses the `useSharedState` hook to access and update the global state. ```typescript import React from 'react'; import { useSharedState } from './store'; const Counter = () => { const [state, setState] = useSharedState(); const increment = () => { setState(prev => ({ ...prev, count: prev.count + 1 })); }; return (
{state.count}
); }; export default Counter; ``` -------------------------------- ### Show Person Component with Conditional Display Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-01.md The `ShowPerson` component displays the person's name. It uses `useTrackedState` to get the name from the store and includes a button to toggle between showing only the first name or the full name. ```typescript import React, { useState } from 'react'; import { useDispatch, useTrackedState } from './store'; import { useFlasher } from './utils'; const ShowPerson = () => { const state = useTrackedState(); const [onlyFirstName, setOnlyFirstName] = useState(false); return (
{onlyFirstName ? (
First Name: {state.firstName}
) : (
Full Name: {state.firstName} {state.lastName}
)}
); }; export default ShowPerson; ``` -------------------------------- ### State Management Store (store.js) Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-03.md Defines the application's state using `useState` and `createContainer` from `react-tracked`. It includes initial state for todos and a query, and exports a custom hook `useSetDraft` for immutable state updates with Immer. ```typescript import { useState, useCallback } from 'react'; import { createContainer } from 'react-tracked'; import produce, { Draft } from 'immer'; type TodoType = { id: number; title: string; completed?: boolean; }; export type State = { todos: TodoType[]; query: string; }; const initialState: State = { todos: [ { id: 1, title: 'Wash dishes' }, { id: 2, title: 'Study JS' }, { id: 3, title: 'Buy ticket' }, ], query: '', }; const useValue = () => useState(initialState); const { Provider, useTrackedState, useUpdate: useSetState, } = createContainer(useValue); const useSetDraft = () => { const setState = useSetState(); return useCallback( (draftUpdater: (draft: Draft) => void) => { setState(produce(draftUpdater)); }, [setState], ); }; export { Provider, useTrackedState, useSetDraft }; ``` -------------------------------- ### Implement Todo Store with Async Actions Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-04.md This snippet defines the state, actions, and reducer for a todo application. It uses `use-reducer-async` to combine synchronous reducer logic with asynchronous action handlers for API interactions. The store manages todo items, their IDs, and a query, supporting creation, updates, and deletions. ```typescript import { Reducer } from 'react'; import { useReducerAsync, AsyncActionHandlers } from 'use-reducer-async'; import { createContainer } from 'react-tracked'; type TodoType = { id: string; title: string; completed?: boolean; }; type State = { todoIds: string[]; todoMap: { [id: string]: TodoType }; query: string; pending: boolean; error: Error | null; }; const initialState: State = { todoIds: [], todoMap: {}, query: '', pending: false, error: null, }; type Action = | { type: 'STARTED' } | { type: 'TODO_CREATED'; todo: TodoType } | { type: 'TODO_UPDATED'; todo: TodoType } | { type: 'TODO_DELETED'; id: string } | { type: 'FAILED'; error: Error } | { type: 'QUERY_CHANGED'; query: string }; const reducer: Reducer = (state, action) => { switch (action.type) { case 'STARTED': return { ...state, pending: true, }; case 'TODO_CREATED': return { ...state, todoIds: [...state.todoIds, action.todo.id], todoMap: { ...state.todoMap, [action.todo.id]: action.todo }, pending: false, }; case 'TODO_UPDATED': return { ...state, todoMap: { ...state.todoMap, [action.todo.id]: action.todo }, pending: false, }; case 'TODO_DELETED': { const { [action.id]: _removed, ...rest } = state.todoMap; return { ...state, todoIds: state.todoIds.filter((id) => id !== action.id), todoMap: rest, pending: false, }; } case 'FAILED': return { ...state, pending: false, error: action.error, }; case 'QUERY_CHANGED': return { ...state, query: action.query, }; default: throw new Error('unknown action type'); } }; type AsyncActionCreate = { type: 'CREATE_TODO'; title: string }; type AsyncActionToggle = { type: 'TOGGLE_TODO'; id: string }; type AsyncActionDelete = { type: 'DELETE_TODO'; id: string }; type AsyncAction = AsyncActionCreate | AsyncActionDelete | AsyncActionToggle; const asyncActionHandlers: AsyncActionHandlers< Reducer, AsyncAction > = { CREATE_TODO: ({ dispatch }) => async (action) => { try { dispatch({ type: 'STARTED' }); const response = await fetch(`https://reqres.in/api/todos?delay=1`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ title: action.title }), }); const data = await response.json(); if (typeof data.id !== 'string') throw new Error('no id'); if (typeof data.title !== 'string') throw new Error('no title'); dispatch({ type: 'TODO_CREATED', todo: data }); } catch (error) { dispatch({ type: 'FAILED', error }); } }, TOGGLE_TODO: ({ dispatch, getState }) => async (action) => { try { dispatch({ type: 'STARTED' }); const todo = getState().todoMap[action.id]; const body = { ...todo, completed: !todo.completed, }; const response = await fetch( `https://reqres.in/api/todos/${action.id}?delay=1`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(body), }, ); const data = await response.json(); if (typeof data.title !== 'string') throw new Error('no title'); dispatch({ type: 'TODO_UPDATED', todo: { ...data, id: action.id } }); } catch (error) { dispatch({ type: 'FAILED', error }); } }, DELETE_TODO: ({ dispatch }) => async (action) => { try { dispatch({ type: 'STARTED' }); await fetch(`https://reqres.in/api/todos/${action.id}?delay=1`, { method: 'DELETE', }); dispatch({ type: 'TODO_DELETED', id: action.id }); } catch (error) { dispatch({ type: 'FAILED', error }); } }, }; const useValue = () => useReducerAsync, AsyncAction>( reducer, initialState, asyncActionHandlers, ); export const { Provider, useTrackedState, useUpdate: useDispatch, } = createContainer(useValue); ``` -------------------------------- ### React Text Box Component Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/quick-start.md A React component that displays and allows editing of a text value from the global state. It uses the `useSharedState` hook to access and update the global state. ```typescript import React from 'react'; import { useSharedState } from './store'; const TextBox = () => { const [state, setState] = useSharedState(); const setText = (text: string) => { setState(prev => ({ ...prev, text })); }; return (
{state.text} setText(e.target.value)} />
); }; export default TextBox; ``` -------------------------------- ### React Root Component with Provider Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-04.md The root component of the ToDo application. It sets up the context provider for the application's state management and renders the main TodoList component. This component is essential for initializing the react-tracked store. ```typescript import React from 'react'; import { Provider } from './store'; import TodoList from './TodoList'; const App = () => ( ); export default App; ``` -------------------------------- ### Get Untracked Object in React Component Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/api.md Demonstrates how to use the getUntrackedObject function within a React component to retrieve the original object before dispatching an action. This is useful to avoid proxy leakage when passing state values to dispatch. ```javascript import { getUntrackedObject } from 'react-tracked'; const Component = () => { const state = useTrackedState(); const dispatch = useUpdate(); const onClick = () => { // this leaks a proxy outside render dispatch({ type: 'FOO', value: state.foo }); // this works as expected dispatch({ type: 'FOO', value: getUntrackedObject(state.foo) }); }; // ... }; ``` -------------------------------- ### useReducer with createContainer (props) Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/recipes.md Demonstrates the typical usage of createContainer with useReducer, passing the reducer and initial state as props. This pattern is flexible for defining generic reducers. ```javascript const { Provider, useTracked, // ... } = createContainer(({ reducer, initialState, init }) => useReducer(reducer, initialState, init)); const reducer = ...; const App = ({ initialState }) => ( ... ); ``` -------------------------------- ### React TodoItem Component Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-02.md The `TodoItem` component renders a single to-do item with a checkbox, its title, and a delete button. It utilizes `useDispatch` and `useTrackedState` to interact with the store. The title is highlighted based on a query if the item is not completed. It also uses a `useFlasher` hook (presumably for visual feedback) and is memoized for performance. ```typescript import React from 'react'; import { useDispatch, useTrackedState, TodoType } from './store'; import { useFlasher } from './utils'; const renderHighlight = (title: string, query: string) => { if (!query) return title; const index = title.indexOf(query); if (index === -1) return title; return ( <> {title.slice(0, index)} {query} {title.slice(index + query.length)} ); }; type Props = TodoType; const TodoItem = ({ id, title, completed }: Props) => { const dispatch = useDispatch(); const state = useTrackedState(); const delTodo = () => { dispatch({ type: 'DELETE_TODO', id }); }; return (
  • dispatch({ type: 'TOGGLE_TODO', id })} /> {completed ? title : renderHighlight(title, state.query)}
  • ); }; export default React.memo(TodoItem); ``` -------------------------------- ### React-Tracked Store with useReducer Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-02.md Defines the shared state management for a ToDo application using `useReducer` and `react-tracked`'s `createContainer`. It includes types for todos and actions, an initial state, and a reducer function to handle state updates. The `createContainer` utility exposes `Provider`, `useTrackedState`, and `useUpdate` (renamed to `useDispatch`). ```typescript import { useReducer } from 'react'; import { createContainer } from 'react-tracked'; export type TodoType = { id: number; title: string; completed?: boolean; }; type State = { todos: TodoType[]; query: string; }; type Action = | { type: 'ADD_TODO'; title: string } | { type: 'DELETE_TODO'; id: number } | { type: 'TOGGLE_TODO'; id: number } | { type: 'SET_QUERY'; query: string }; const initialState: State = { todos: [ { id: 1, title: 'Wash dishes' }, { id: 2, title: 'Study JS' }, { id: 3, title: 'Buy ticket' }, ], query: '', }; let nextId = 4; const reducer = (state: State, action: Action): State => { switch (action.type) { case 'ADD_TODO': return { ...state, todos: [...state.todos, { id: nextId++, title: action.title }], }; case 'DELETE_TODO': return { ...state, todos: state.todos.filter((todo) => todo.id !== action.id), }; case 'TOGGLE_TODO': return { ...state, todos: state.todos.map((todo) => todo.id === action.id ? { ...todo, completed: !todo.completed } : todo, ), }; case 'SET_QUERY': return { ...state, query: action.query, }; default: return state; } }; const useValue = () => useReducer(reducer, initialState); export const { Provider, useTrackedState, useUpdate: useDispatch, } = createContainer(useValue); ``` -------------------------------- ### useTrackedStore in a Component (React) Source: https://github.com/dai-shi/react-tracked/blob/main/README.md Demonstrates how to use the `useTrackedStore` hook within a React functional component to access and update the global state. It shows a simple counter example with a button to increment the state. ```jsx const Counter = () => { const state = useTrackedStore(); const increment = () => { useStore.setState((prev) => ({ count: prev.count + 1 })); }; return (
    Count: {state.count}
    ); }; ``` -------------------------------- ### useReducer with event listener (window resize) Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/recipes.md Demonstrates dispatching actions to a useReducer hook based on DOM events, specifically the window resize event. It sets up and cleans up an event listener within useEffect. ```javascript const reducer = ...; const initialState = ...; const useValue = () => { const [state, dispatch] = useReducer(reducer, initialState); useEffect(() => { const listener = () => { dispatch({ type: 'WINDOW_RESIZED', width: window.innerWidth, height: window.innerHeight, }); }; window.addEventListener('resize', listener); return () => { window.removeEventListener('resize', listener); }; }, []); return [state, dispatch]; }; const { Provider, useTracked, // ... } = createContainer(useValue); const App = () => ( ... ); ``` -------------------------------- ### React Counter Component with Random Number Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/quick-start.md Modified Counter component to include `Math.random()` in its JSX. This helps visualize performance differences, showing that the random number only updates when the counter state changes in the React Tracked version. ```typescript const Counter = () => { const [state, setState] = useSharedState(); const increment = () => { setState(prev => ({ ...prev, count: prev.count + 1 })); }; return (
    {state.count} {Math.random()}
    ); }; ``` -------------------------------- ### React App Root Component Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-01.md The root `App` component sets up the `Provider` from `react-tracked` and renders the `EditPerson` and `ShowPerson` components. ```typescript import React from 'react'; import { Provider } from './store'; import EditPerson from './EditPerson'; import ShowPerson from './ShowPerson'; const App = () => ( ); export default App; ``` -------------------------------- ### useState with createContainer (props) Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/recipes.md Illustrates a simpler usage pattern with useState when a reducer is not required. The initial state is passed as a prop to the Provider. ```javascript const { Provider, useTracked, // ... } = createContainer(({ initialState }) => useState(initialState); const App = ({ initialState }) => ( ... ); ``` -------------------------------- ### React TodoList Component Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-02.md The `TodoList` component displays a list of `TodoItem`s, a `NewTodo` input for adding new items, and a text field to filter todos by a query. It uses `useDispatch` to dispatch actions and `useTrackedState` to access the application's state, including the list of todos and the current query. ```typescript import React from 'react'; import { useDispatch, useTrackedState } from './store'; import TodoItem from './TodoItem'; import NewTodo from './NewTodo'; const TodoList = () => { const dispatch = useDispatch(); const state = useTrackedState(); const setQuery = (event: React.ChangeEvent) => { dispatch({ type: 'SET_QUERY', query: event.target.value }); }; return (
      {state.todos.map(({ id, title, completed }) => ( ))}
    Highlight Query for incomplete items:
    ); }; export default TodoList; ``` -------------------------------- ### React App Root Component Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-02.md The root `App` component in a React application. It sets up the `Provider` from `react-tracked` to make the shared state available to its descendants, wrapping the main `TodoList` component. ```typescript import React from 'react'; import { Provider } from './store'; import TodoList from './TodoList'; const App = () => ( ); export default App; ``` -------------------------------- ### NewTodo Component for Adding Items (React/TypeScript) Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-02.md The NewTodo component is responsible for creating and adding new items. It utilizes local state management for the input field and dispatches an 'ADD_TODO' action to the store. It depends on the `useDispatch` hook from './store' and the `useFlasher` hook from './utils'. ```typescript import React, { useState } from 'react'; import { useDispatch } from './store'; import { useFlasher } from './utils'; const NewTodo = () => { const dispatch = useDispatch(); const [text, setText] = useState(''); const addTodo = () => { dispatch({ type: 'ADD_TODO', title: text }); setText(''); }; return (
  • setText(e.target.value)} />
  • ); }; export default React.memo(NewTodo); ``` -------------------------------- ### TodoItem Component (React) Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-03.md The TodoItem component displays a single todo item with a checkbox for completion status, the todo title (with highlighting for search queries), and a delete button. It utilizes custom hooks for data fetching, deletion, and toggling completion status. Dependencies include React, useQuery, useDeleteTodo, useToggleTodo, and useFlasher. ```typescript import React from 'react'; import { useQuery } from '../hooks/useQuery'; import { useDeleteTodo } from '../hooks/useDeleteTodo'; import { useToggleTodo } from '../hooks/useToggleTodo'; import { useFlasher } from '../utils'; const renderHighlight = (title, query) => { if (!query) return title; const index = title.indexOf(query); if (index === -1) return title; return ( <> {title.slice(0, index)} {query} {title.slice(index + query.length)} ); }; type Props = { id: number; title: string; completed?: boolean; }; const TodoItem = ({ id, title, completed }: Props) => { const { getQuery } = useQuery(); const deleteTodo = useDeleteTodo(); const toggleTodo = useToggleTodo(); return (
  • toggleTodo(id)} /> {completed ? title : renderHighlight(title, getQuery())}
  • ); }; export default React.memo(TodoItem); ``` -------------------------------- ### React Todo List Component Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-04.md Displays a list of todo items, a new todo input, and a query filter. It handles displaying errors and pending states, and passes only the 'id' to each TodoItem. It uses hooks from './store' for dispatching actions and accessing tracked state. ```typescript import React from 'react'; import { useDispatch, useTrackedState } from './store'; import TodoItem from './TodoItem'; import NewTodo from './NewTodo'; const TodoList = () => { const dispatch = useDispatch(); const state = useTrackedState(); const setQuery = (event: React.ChangeEvent) => { dispatch({ type: 'QUERY_CHANGED', query: event.target.value }); }; return (
    {state.error &&

    {state.error.message}

    }
      {state.todoIds.map(id => ( ))}
    Highlight Query for incomplete items:
    {state.pending &&

    Processing...

    }
    ); }; export default TodoList; ``` -------------------------------- ### Custom Hook for Querying Todos (useQuery.js) Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-03.md A hook to manage the search query for filtering todos. It provides `getQuery` to retrieve the current query and `setQuery` to update it immutably. ```typescript import { useCallback } from 'react'; import { useTrackedState, useSetDraft } from '../store'; export const useQuery = () => { const state = useTrackedState(); const getQuery = () => state.query; const setDraft = useSetDraft(); const setQuery = useCallback( (query: string) => { setDraft((draft) => { draft.query = query; }); }, [setDraft], ); return { getQuery, setQuery }; }; ``` -------------------------------- ### React New Todo Component Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-04.md A component for adding new todo items. It features an input field for the todo title and an 'Add' button. It manages its own local state for the input text and dispatches a 'CREATE_TODO' action upon adding. This component is also memoized. ```typescript import React, { useState } from 'react'; import { useDispatch } from './store'; const NewTodo = () => { const dispatch = useDispatch(); const [text, setText] = useState(''); const addTodo = () => { dispatch({ type: 'CREATE_TODO', title: text }); setText(''); }; return (
  • setText(e.target.value)} />
  • ); }; export default React.memo(NewTodo); ``` -------------------------------- ### NewTodo Component (React) Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-03.md The NewTodo component allows users to add new todo items to the list. It features an input field for the todo title and an 'Add' button. The component manages its input state locally and uses the useAddTodo hook to persist the new todo. Dependencies include React, useState, useAddTodo, and useFlasher. ```typescript import React, { useState } from 'react'; import { useAddTodo } from '../hooks/useAddTodo'; import { useFlasher } from '../utils'; const NewTodo = () => { const addTodo = useAddTodo(); const [text, setText] = useState(''); return (
  • setText(e.target.value)} />
  • ); }; export default React.memo(NewTodo); ``` -------------------------------- ### Implement useTrackedSelector with Redux Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-redux-01.md This snippet demonstrates setting up a Redux store with a simple reducer and integrating it with `react-tracked`'s `createTrackedSelector`. It defines state, actions, and a reducer for managing first and last names. The `useTrackedSelector` hook is then used within React components to access and subscribe to specific parts of the Redux state, optimizing re-renders. ```typescript import * as React from "react"; import { useState } from "react"; import { createStore } from "redux"; import { Provider, useDispatch, useSelector } from "react-redux"; import { createTrackedSelector } from "react-tracked"; const initialState = { firstName: "React", lastName: "Tracked" }; type State = typeof initialState; type Action = | { type: "setFirstName"; firstName: string } | { type: "setLastName"; lastName: string }; const reducer = (state = initialState, action: Action) => { switch (action.type) { case "setFirstName": return { ...state, firstName: action.firstName }; case "setLastName": return { ...state, lastName: action.lastName }; default: return state; } }; const store = createStore(reducer); const useTrackedSelector = createTrackedSelector(useSelector); const EditPerson = () => { const dispatch = useDispatch(); const state = useTrackedSelector(); const setFirstName = (e: React.ChangeEvent) => { const firstName = e.target.value; dispatch({ type: "setFirstName", firstName }); }; const setLastName = (e: React.ChangeEvent) => { const lastName = e.target.value; dispatch({ type: "setLastName", lastName }); }; return (
    First Name:
    Last Name:
    ); }; const ShowPerson = () => { const state = useTrackedSelector(); const [onlyFirstName, setOnlyFirstName] = useState(false); return (
    {onlyFirstName ? (
    First Name: {state.firstName}
    ) : (
    Full Name: {state.firstName} {state.lastName}
    )}
    ); }; const App = () => { return ( ); }; export default App; ``` -------------------------------- ### Utility Hook for Visual Rendering Feedback Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-01.md The `useFlasher` hook is a custom utility that adds a red box-shadow to an element for a short duration when it renders, providing visual feedback on component updates. It uses `useRef` and `useEffect`. ```typescript import { useRef, useEffect } from 'react'; export const useFlasher = () => { const ref = useRef(null); useEffect(() => { if (!ref.current) return; ref.current.setAttribute( 'style', 'box-shadow: 0 0 2px 1px red; transition: box-shadow 100ms ease-out;', ); setTimeout(() => { if (!ref.current) return; ref.current.setAttribute('style', ''); }, 300); }); return ref; }; ``` -------------------------------- ### React Todo Item Component Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-04.md Represents a single todo item in the list. It allows toggling completion status and deleting the item. It also highlights the query term within the todo title if a query is active. This component is memoized for performance. It dispatches actions asynchronously without needing to know the action's sync/async nature. ```typescript import React from 'react'; import { useDispatch, useTrackedState } from './store'; const renderHighlight = (title: string, query: string) => { if (!query) return title; const index = title.indexOf(query); if (index === -1) return title; return ( <> {title.slice(0, index)} {query} {title.slice(index + query.length)} ); }; type Props = { id: string; }; const TodoItem = ({ id }: Props) => { const dispatch = useDispatch(); const state = useTrackedState(); const todo = state.todoMap[id]; const delTodo = () => { dispatch({ type: 'DELETE_TODO', id: todo.id }); }; return (
  • dispatch({ type: 'TOGGLE_TODO', id: todo.id })} /> {todo.completed ? todo.title : renderHighlight(todo.title, state.query)}
  • ); }; export default React.memo(TodoItem); ``` -------------------------------- ### useReducer with persistence (localStorage) Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/recipes.md Provides a recipe for persisting state to localStorage using useReducer and createContainer. It includes logic to load state from localStorage or use an initial state if localStorage is empty. ```javascript const reducer = ...; const initialState = ...; // used only if localStorage is empty. const storageKey = 'persistedState'; const init = () => { let preloadedState; try { preloadedState = JSON.parse(window.localStorage.getItem(storageKey)); // validate preloadedState if necessary } catch (e) { // ignore } return preloadedState || initialState; }; const useValue = () => { const [state, dispatch] = useReducer(reducer, null, init); useEffect(() => { window.localStorage.setItem(storageKey, JSON.stringify(state)); }, [state]); return [state, dispatch]; }; const { Provider, useTracked, // ... } = createContainer(useValue); const App = () => ( ... ); ``` -------------------------------- ### TodoList Component Display Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/tutorial-03.md Renders the list of todos, the input for adding new todos, and a search input to filter incomplete items. It utilizes custom hooks for data fetching and state updates. ```typescript import React from 'react'; import { useTodoList } from '../hooks/useTodoList'; import { useQuery } from '../hooks/useQuery'; import TodoItem from './TodoItem'; import NewTodo from './NewTodo'; const TodoList = () => { const { getQuery, setQuery } = useQuery(); const todos = useTodoList(); return (
      {todos.map(({ id, title, completed }) => ( ))}
    Highlight Query for incomplete items: setQuery(e.target.value)} />
    ); }; export default TodoList; ``` -------------------------------- ### Create Container for Context-like Usage Source: https://github.com/dai-shi/react-tracked/blob/main/website/docs/api.md A higher-level function to create a React Context provider and associated hooks for state management. It takes a useValue hook (returning state and update) and optional configuration options. ```typescript type Options = { defaultState?: State; defaultUpdate?: Update; stateContextName?: string; updateContextName?: string; concurrentMode?: boolean; }; // Example usage with useReducer: import { createContainer } from 'react-tracked'; import { useReducer } from 'react'; const useValue = (props) => useReducer(...); const { Provider, useTracked, useUpdate, useTrackedState, useSelector, } = createContainer(useValue); ```