react-store-input
v0.1.7
Published
The goal of this package is to make state management easier when using input elements in React.
Readme
React Store Input
The goal of this package is to make state management easier when using input elements in React.
It eliminates repetitive code required to implement state changes and subscriptions for input elements, and provides a simple interface.
At the same time, it allows you to use all the attributes originally provided by the input tag as-is, without needing to learn this package.
Get Started
This is a simple example of how to use this package.
import { useFormStore } from "dn-react-input";
export default function App() {
const store = useFormStore({
email: "",
password: "",
});
const submit = async () => {
const { email, password } = store.state;
alert(`Email: ${email}\nPassword: ${password}`);
};
return (
<form
onSubmit={(e) => {
e.preventDefault();
submit();
}}
>
<store.input name="email" type="email" />
<store.input name="password" type="password" />
<button type="submit">Submit</button>
</form>
);
}How to define state?
You can define any state you want as an object when calling useStore.
function Component() {
...
const store = useStore({
email: "",
password: "",
rememberMe: false,
});
...
}It's a single source of truth for your form state.
How to get input values?
You can access the current values of the input elements through the state property of the store.
function Component() {
...
const submit = () => {
const { email, password, rememberMe } = store.state;
};
...
}How to add input elements?
You can add input elements using the Input component provided by the store. There are 'Select' and 'Textarea' components as well.
import { Input } from "dn-react-input";
function Component() {
...
return (
<form>
<Input store={store} name="email" type="email" />
<Input store={store} name="password" type="password" />
<Input store={store} name="rememberMe" type="checkbox" />
</form>
);
}If you want to avoid passing the store to each input component, use useStoreComponent. This hook provides input components that are already connected to the store.
import { useStoreComponent } from "dn-react-input";
function Component() {
...
const component = useStoreComponent(store);
return (
<form>
<component.input name="email" type="email" />
<component.input name="password" type="password" />
<component.input name="rememberMe" type="checkbox" />
</form>
);
}useFormStore is a facade that combines useStore and useStoreInput for convenience.
import { useFormStore } from "dn-react-input";
function Component() {
...
const store = useFormStore({
email: "",
password: "",
rememberMe: false,
});
return (
<form>
<store.input name="email" type="email" />
<store.input name="password" type="password" />
<store.input name="rememberMe" type="checkbox" />
</form>
);
}How to render components on state changes?
If you want to render a component only when specific parts of the state change, use the useSelector hook.
import { useSelector } from "dn-react-input";
function Component() {
...
const email = useSelector(store, (state) => state.email);
return <div>Your email is: {email}</div>;
}If you want to render components in an inline manner, use the createRender function. By using this, you can avoid creating separate components for each part of the state you want to track.
import { createRender } from "dn-react-input";
function Component() {
...
return (
<div>
{createRender(store, (state) => <p>{state.email}</p>)}
{createRender(store, (state) => <p>{state.password}</p>)}
</div>
);
}Store.render is a shortcut for createRender when you use useFormStore.
function Component() {
const store = useFormStore({
email: "",
password: "",
});
return (
<div>
{store.render((state) => (
<p>{state.email}</p>
))}
{store.render((state) => (
<p>{state.password}</p>
))}
</div>
);
}How to subscribe to state changes?
You can subscribe to state changes using the subscribe method of the store.
function Component() {
...
useEffect(() => {
const unsubscribe = store.subscribe((state) => {
console.log(`State changed`, state);
});
return () => {
unsubscribe();
};
}, []);
...
}How to update state manually?
You can update the state manually using the dispatch method of the store.
function Component() {
...
const updateEmail = () => {
store.dispatch({ email: "[email protected]" });
};
return <button onClick={updateEmail}>Update Email</button>;
}The dispatch method uses immerjs internally to update the state, so you can also use a function to update the state based on the previous state.
function Component() {
...
const updateEmail = () => {
store.dispatch((state) => {
state.email = "[email protected]";
});
};
return <button onClick={updateEmail}>Update Email</button>;
}How to create custom input components?
You can create custom input components using the useStoreInput hook. This hook provides the necessary props to connect your custom input component to the store: name, value, defaultValue, defaultChecked, onChange, and ref which already subscribed to the store.
import { useStoreInput } from "dn-react-input";
function CustomInput({ store }: { store: Store<{ email: string }> }) {
const inputProps = useStoreInput(store, {
name: "email",
});
return <input {...inputProps} />;
}How to creatre custom controller components?
If your custom component is not an html input element, you can use the useStoreController hook. This hook provides the necessary props to connect your custom controller component to the store: ref, onSubscribe, and onDispatch.
import { useStoreController } from "dn-react-input";
type State = {
count: number;
};
function CustomController({ store }: { store: Store<State> }) {
const controllerProps = useStoreController<HTMLDivElement, State>(store, {
onSubscribe: (state, element) => {
element.textContent = `Count: ${state.count}`;
},
onDispatch: (state, element) => {
state.count += Number(element.textContent.replace("Count: ", ""));
},
});
return <div {...controllerProps} />;
}