Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ncpa0/react-better-components


https://github.com/ncpa0/react-better-components

Last synced: about 2 months ago
JSON representation

Awesome Lists containing this project

README

        

# React Better Components

## Usage

```tsx
import { BetterComponent } from "react-better-components";

class BtnClickCounter extends BetterComponent {
private counter = this.$state(0);

private handleClick = () => {
this.counter.set((c) => c + 1);
};

public render() {
return (



Clicked {this.counter.get()} times


);
}
}
```

### Reducers

Reducer is a state coupled with functions for mutating it's state.

```tsx
import { BetterComponent } from "react-better-components";

class NumberReducer {
public increment(state: number) {
return state + 1;
}

public decrement(state: number) {
return state - 1;
}

public add(state: number, amount: number) {
return state + amount;
}
}

class NumberView extends BetterComponent {
private number = this.$reducer(0, NumberReducer);

public render() {
const increment = () => this.number.increment();
const decrement = () => this.number.decrement();
const addTen = () => this.number.add(10);
return (


+1
-1
+10
{this.number.get()}


);
}
}
```

### Computed Values

Computed values are values that are derived from other reactive values. Whenever it dependency changes, the
computed value will be re-calculated.

```tsx
import { BetterComponent } from "react-better-components";

type Props = {
multiplyBy: number;
};

class MyComponent extends BetterComponent {
private value = this.$state(0);

private computedValue = this.$computed(() => {
// This calculation will re-run whenever this.value or
// this.props.multiplyBy changes.
return this.value.get() * this.props.multiplyBy;
}, [this.value, this.depends.multiplyBy]);

public render() {
return

{this.computedValue.get()}
;
}
}
```

### Effects

Effects are functions that run when a reactive value changes.

```tsx
import { BetterComponent } from "react-better-components";

type Props = {
value: number;
};

class MyComponent extends BetterComponent {
private internalValue = this.$state(0);

public constructor(props: Props) {
super(props);

this.$effect(() => {
// This will run on mount and whenever this.props.value changes.
}, [this.depends.value]);

this.$effect(() => {
// This will run on mount and whenever this.internalValue changes.
}, [this.internalValue]);

this.$effect(() => {
// This will run on first mount
}, []);

this.$effect(() => {
// This will run on every update
});
}

public render() {
return (


{this.props.value}

{this.internalValue.get()}


);
}
}
```

### External Stores

External stores are reactive values that are not owned by the component.

```tsx
import { BetterComponent } from "react-better-components";

// An example store
class Store extends EventTarget {
private state: T;

public constructor(initialState: T) {
super();
this.state = initialState;
}

public set(v: T) {
this.state = v;
this.dispatchEvent(new Event("change"));
}

public get() {
return this.state;
}

public subscribe(cb: () => void) {
this.addEventListener("change", cb);
return () => this.removeEventListener("change", cb);
}
}

const store = new Store(1);

class MyComponent extends BetterComponent {
private externalValue = this.$externalStore(
(cb) => store.subscribe(cb),
() => store.get(),
);

public render() {
return

{this.externalValue.get()}
;
}
}
```

### Composition (Component Modules)

Component Modules are a way to compose components from smaller classes that have the same API available to them as BetterComponents.

```tsx
import {
BetterComponent,
ComponentModule,
Dependency,
} from "react-better-components";

/**
* A component module that fetches data from a url. url can be any
* reactive value, whenever it changes the data will be fetched
* again.
*/
class UrlFetch extends ComponentModule<[Dependency]> {
public isLoading = this.$state(false);
public result = this.$state(null);
public error = this.$state(null);

public constructor(params: any) {
super(params);

const [url] = this.args;

this.$effect(() => {
this.fetchFromUrl();
}, [url]);
}

private fetchFromUrl() {
const [url] = this.args;

this.isLoading.set(true);

fetch(url.get())
.then((res) => res.json())
.then((data) => {
this.result.set(data);
this.error.set(null);
this.isLoading.set(false);
})
.catch((err) => {
this.result.set(null);
this.error.set(err);
this.isLoading.set(false);
});
}
}

type Props = {
url: string;
};

class MyComponent extends BetterComponent {
private data = this.$mod(UrlFetch, this.depends.url);

public render() {
if (this.data.isLoading.get()) {
return

Loading...
;
}

if (this.data.error.get()) {
return (

Error: {this.data.error.get()}

);
}

return

{this.data.result}
;
}
}
```