0.1.4 • Published 6 months ago

litstate-app v0.1.4

Weekly downloads
-
License
MIT
Repository
github
Last release
6 months ago

litState

Overview

litState is a lightweight TypeScript framework for creating web applications quickly and efficiently. It provides:

  • Zero dependencies: Streamlines your project setup.
  • Simple API: Easy to learn and use.
  • Reactive Components: Optimize your UI with precise updates.
  • State Management: Utilize proxies for effortless state handling.
  • Fast Project Setup: Ideal for small projects with its quick setup.

Check out the full documentation or for more details.

Installation

To get started, install litState using npm:

npm install litstate-app

Usage

Setting Up Global State

Import and create a global state object:

import { createState } from 'litstate-app';

const initialState = {
  count: 0,
};

export const globalState = createState(initialState);

Creating Components

Build components that interact with the global state:

import { component, html, handler } from 'litstate-app';
import { globalState } from './globalState';

export const Counter = component(() => {
  return html`
    <div>
      <p>Count: ${globalState.count}</p>
    </div>
  `;
});

export const IncrementButton = component(() => {
  const incrementHandler = handler(() => {
    globalState.count++;
  });

  return html`
    <div>
      <button onclick="${incrementHandler}">Increment</button>
    </div>
  `;
});

Root Component

Create a root component to render other components:

import { component, html } from 'litstate-app';
import { Counter, IncrementButton } from './components';

export const App = component(() => {
  return html` <div>${Counter()} ${IncrementButton()}</div> `;
});

Mounting the App

Mount the root component to the DOM:

import { mount } from 'litstate-app';
import { App } from './App';

// HTML element where the app will mount
// <div id="app"></div>;

// Mount the App component to the 'app' element
mount(App, 'app');

Component Function

Define a reusable UI component:

export const MyComponent = component(({ title, content }) => {
  return html`
    <div>
      <h1>${title}</h1>
      <p>${content}</p>
    </div>
  `;
});

Instantiate the component with the desired props:

import { MyComponent } from './MyComponent';

export const App = component(() => {
  return html`
    <div>${MyComponent({ title: 'My Title', content: 'My Content' })}</div>
  `;
});

The component's unique identifier, id, can be included in the props object. If not provided, the id is automatically generated based on the component's position in the call stack and is appended to the props. When defining components that may be instantiated multiple times within a loop, it's important to provide an id prop to ensure each instance maintains a unique identity:

import { component, html } from '../../../src';

export const LoopComponent = component(props => {
  const { id } = props; // Destructure the id from props if needed

  return html`
    <div>
      <!-- some HTML -->
    </div>
  `;
});

// Use the component in a loop with unique 'id' props
html`
  ${yourArray.map((item, index) =>
    LoopComponent({ ...item, id: item.uniqueId || index })
  )}
`;

State Management

litState provides a straightforward state management system designed to enable both global and local states, leveraging the power of JavaScript proxies for reactivity. These states are deeply reactive due to the recursive use of proxies, ensuring updates are precise and components re-render only when necessary.

import {
  createState,
  addListener,
  removeListener,
  batchUpdate,
  html,
} from 'litstate-app';

// Create a global state object
export const appState = createState({
  user: {
    name: 'Jane Doe',
    age: 25,
  },
  theme: 'light',
  isLoggedIn: false,
});

// Create a component that uses the global and local states
export const MyComponent = component(() => {
  // Create a local state object
  const localState = createState({
    count: 0,
  });

  const incrementHandler = handler(() => {
    // Update the local state
    localState.count++;
  });

  const userNameSetter = handler(() => {
    // Update the global state
    appState.user.name = 'John Doe';
  });

  return html`
    <div>
      <p>Global state: ${appState.user.name}</p>
      <p>Local state: ${localState.count}</p>
      <button onclick="${incrementHandler}">Increment</button>
      <button onclick="${userNameSetter}">Set User Name</button>
    </div>
  `;
});

State updates can be done from anywhere in your application, including outside of components:

import { appState } from './appState';

// Update the global state
appState.user.name = 'John Doe';

Batch State Updates

Batch state updates are useful to prevent unnecessary re-renders. Multiple state updates can be batched together using the batchUpdate function. Components will only re-render once all state updates are complete.

import { appState } from './appState';
import { batchUpdate } from 'litstate-app';

// Batch state updates
batchUpdate(() => {
  appState.user.name = 'John Doe';
  appState.user.age = 30;
});

Adding and Removing Listeners

To listen for state changes outside of components, use the addListener function. These listeners execute once upon creation, monitoring the accessed state properties, and are reinvoked whenever those properties undergo changes. When creating listeners within loops or multiple instances, ensure to provide a unique id for each listener to maintain the correct tracking and to provide the capability to remove them individually if needed.

import { appState } from './appState';
import { addListener, removeListener } from 'litstate-app';

// Create a listener with an ID
addListener(() => {
  console.log('User name:', appState.user.name);
}, 'user-name-listener'); // The ID is optional unless you need to remove the listener later, or it is within a loop

// Later, remove the listener
removeListener('user-name-listener');

HTML Function

Utilize the html function to create and manage dynamic HTML content:

import { html } from 'litstate-app';

export const Greeting = component(({ name }) => {
  return html` <div>Hello, ${name}!</div> `;
});

Handler Function

Handle events within your components:

export const IncrementButton = component(() => {
  const incrementHandler = handler((event, element) => {
    console.log('Event:', event);
    console.log('Element:', element);
    globalState.count++;
  });

  return html`
    <div>
      <button onclick="${incrementHandler}">Increment</button>
    </div>
  `;
});

Mount Method

Render your component into the DOM:

// In your HTML file
// <div id="app"></div>

import { mount } from 'litstate-app';
import { App } from './App';

mount(App, 'app');

Router and Link Components

Implement client-side routing:

import { Router, Link, navigate } from 'litstate-app/components';
import { Home, About, NotFound } from './components';

const NavBar = () => {
  return html`
    <div>
      ${Link({ to: '/', children: 'Home' })} ${Link({
        to: '/about',
        children: 'About',
      })}
    </div>
  `;
};

const routes = {
  '/': Home,
  '/about': About,
  '*': NotFound,
};

const App = () => {
  return html`
    <div>
      <Router routes=${routes} />
    </div>
  `;
};

// Use navigate to change routes programmatically
navigate('/about');

License

MIT License

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.