Hone logo
Hone
Problems

Reactive State Management with Signals in React

Signals provide a fine-grained reactivity system, allowing components to react only to the specific state changes they depend on. This challenge asks you to implement a basic signals system within a React context, enabling efficient and targeted re-renders. This is a modern approach to state management, offering performance benefits over traditional methods like useState when dealing with complex state dependencies.

Problem Description

You are tasked with creating a createSignal function and a useSignal hook that implement a simple signals system. The createSignal function should take an initial value and return an object with two properties: value (the current signal value) and set (a function to update the signal value). The useSignal hook should accept a signal object and return the signal's value. When the set function is called, all components using the signal via useSignal should re-render.

Key Requirements:

  • createSignal(initialValue: T): { value: T; set: (newValue: T) => void }: Creates a new signal with the given initial value. The set function should accept a new value of the same type T.
  • useSignal<T>(signal: { value: T; set: (newValue: T) => void }): T: A React hook that subscribes to a signal and returns its current value. It should trigger a re-render of the component whenever the signal's value changes.
  • Reactivity: Components using useSignal must re-render only when the signal's value changes.
  • Type Safety: The signals system should be type-safe, ensuring that the set function only accepts values of the correct type.

Expected Behavior:

  1. Calling createSignal should create a signal object with a value property initialized to the provided initialValue and a set function.
  2. Calling the set function should update the signal's value and trigger re-renders in all components using useSignal with that signal.
  3. useSignal should return the current value of the signal.
  4. The system should be type-safe, preventing incorrect type assignments.

Edge Cases to Consider:

  • Signals with primitive types (number, string, boolean).
  • Signals with object types. (While this challenge doesn't require deep reactivity, consider how changes to properties within an object signal would be handled - a shallow comparison is sufficient for this exercise).
  • Multiple components using the same signal.
  • Updating a signal value with the same value it already holds. (Should not trigger a re-render).

Examples

Example 1:

Input:
createSignal(0); // Creates a signal named 'count' with initial value 0
const countSignal = createSignal(0);

// In a React component:
function MyComponent() {
  const count = useSignal(countSignal);
  return <div>Count: {count}</div>;
}

Output: Initially, MyComponent renders "Count: 0". When countSignal.set(1) is called, MyComponent re-renders to "Count: 1". Explanation: createSignal creates a signal. useSignal subscribes to the signal. Updating the signal's value via set triggers a re-render of MyComponent.

Example 2:

Input:
const nameSignal = createSignal("Alice");

// In a React component:
function Greeting() {
  const name = useSignal(nameSignal);
  return <h1>Hello, {name}!</h1>;
}

nameSignal.set("Bob");

Output: Initially, Greeting renders "Hello, Alice!". After nameSignal.set("Bob"), Greeting re-renders to "Hello, Bob!". Explanation: Similar to Example 1, but with a string signal.

Example 3: (Edge Case)

Input:
const isEnabledSignal = createSignal(true);

// In a React component:
function Toggle() {
  const isEnabled = useSignal(isEnabledSignal);
  return <button onClick={() => isEnabledSignal.set(!isEnabled)}>Toggle</button>;
}

// Initial render: isEnabledSignal.value is true.
// Clicking the button sets isEnabledSignal.value to false.
// Clicking the button again sets isEnabledSignal.value back to true.

Output: The component toggles between displaying the button state based on the signal's value. No unnecessary re-renders occur when setting the value back to its original state. Explanation: Demonstrates toggling a boolean signal and the importance of avoiding re-renders when the value remains unchanged.

Constraints

  • React Version: Assume React 18 or later.
  • Performance: The re-rendering should be efficient. Avoid unnecessary re-renders.
  • Type Safety: The code must be written in TypeScript and maintain type safety.
  • No External Libraries: Do not use any external state management libraries (e.g., Redux, Zustand). You are implementing a simplified signals system.
  • Signal Creation Limit: Assume a maximum of 100 signals will be created concurrently.

Notes

  • Consider using React's useRef to store a list of components that are currently subscribed to a signal.
  • Think about how to efficiently trigger re-renders when a signal's value changes. Directly calling setState on each component might be inefficient.
  • Focus on the core functionality of creating signals and providing reactivity. Advanced features like derived signals or computed values are not required for this challenge.
  • The goal is to demonstrate a basic understanding of how signals work and how they can be integrated into a React application.
Loading editor...
typescript