Hook to connect state chages across React components. No callbacks. No context. No wrappers.
npm install use-connected-state
useConnectedState
, along with useConnectedValue
and useConnectedSetter
, let you connect state
changes across components anywhere in the tree. When state
changes in one component, it will be updated in other components connected to the same state
, triggering a re-render.
Let's say you have a Input
component and another component that shows the number of characters the user entered. You could wrap these in a single component but they may not live by each other in the layout.
// input.jsx
import useConnectedState from 'use-connected-state';
function Input() {
const [chars, setChars] = useConnectedState({ key: 'chars', default: '' });
const onChange = (e) => {
setChars(e.target.value);
};
return (<input type="text" onChange={onChange} value={chars} />);
}
export default Input;
// character-count.jsx
import { useConnectedValue } from 'use-connected-state';
function CharacterCount() {
const chars = useConnectedValue({ key: 'chars', default: '' });
return (<p>Character Count: {chars.length}</p>);
}
export default CharacterCount;
The Input
component when used by itself will work like any normal functional component that uses useState
. You can add the CharacterCount
component anywhere in the tree it will automatically show the count of characters from the Input
component.
Just like useState
, useConnectedState
returns the value and a setter function. Also just like useState
, you can pass the setter function a function which will receive the previous (current really) value as the first argument, but it also gets 2 more arguments: an object containing all of the state
in the current scope
and a setter function that allows you to update any piece of state
by its key
.
Let's say we have a component that shows how many times a counter has been clicked.
// clicks.jsx
import { useConnectedValue } from 'use-connected-state';
function Clicks() {
const clicks = useConnectedValue({ key: 'clicks', default: 0 });
return (<div>Total Clicks: {clicks}</div>);
}
export default Clicks;
Our Counter
component can update the clicks
of the Clicks
component.
// counter.jsx
function Counter() {
const [count, setCount] = useConnectedState({ key: 'count', default: 0 });
const increment = () => {
setCount((val, results, setByKey) => {
// Using results to get the current value of clicks
setByKey('clicks', results.clicks + 1);
return val + 1;
});
};
const decrement = () => {
setCount((val, results, setByKey) => {
// Passing an updater function which gets the current value of clicks
setByKey('clicks', currClicks => currClicks + 1);
return val - 1;
});
};
return (<div>
<div>Count: {count}</div>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>);
}
If the Clicks
component does not exist the setByKey
call is ignored allowing the Counter
to still work.
useConnectedState
hooks take an optional state config object.
Name | Type | Default | Description |
---|---|---|---|
key |
string |
__defaultKey__ |
(Optional) A key for the state . Any componets using useConnectedState with the same key and scope will be updated when state with that key is updated. |
default |
any | undefined |
(Optional) The default state for the given key and scope . The first component for the given key and scope will set the default state for all that follow. Subsequent components do not even need to pass this. As with useState , you can pass a function which returns the default state and it will only be executed on the first render. |
state |
any | undefined |
(Optional) Deprecated - replaced by default which makes more sense. |
scope |
string |
__defaultScope__ |
(Optional) Provides scope for the state . Allows multiple components use the same key for state but not cause updates to each other. |
passive |
boolean |
false |
(Optional) Allows you to passively get and set state . When passive is true , updates to that state will not trigger a re-render of the component. Also, when true the hook returns 2 functions: one to get the current value and one to set the value. |
If you just want set or get the value of state you can use useConnectedSetter
or useConnectedValue
respectively. These are named exports so you would import them like
import { useConnectedSetter } from 'use-connected-state';
They take the same state config object as useConnectedState
. When using useConnectedSetter
you probably don't want that component to update when that state
changes since you're not using the value
, so you may want to add passive: true
to the config.
// click-count.jsx
import { useConnectedValue } from 'use-connected-state';
function ClickCount() {
const clicks = useConnectedValue({ key: 'clicks', default: 0 });
return (<div>Total Clicks: {clicks}</div>);
}
export default ClickCount;
// clicker.jsx
import { useConnectedSetter } from 'use-connected-state';
function Clicker() {
const setClicks = useConnectedSetter({ key: 'clicks', default: 0, passive: true });
const onClick = () => {
setClicks(clicks => clicks + 1);
};
return (<button onClick={onClick}>Click Me</button>);
}
export default Clicker;
The getCurrentState(<scope>)
method that returns all state
for the optionally specified scope
. If scope
is not specified it returns the state
for the default scope
.