Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
react-reactive-hooks
Advanced tools
A set of React hooks to make your components reactive
useReactive
and useReactiveValue
like reactive
and ref
in Vue 3v-model
in Vue - reactive
to create a “reactive” version of a component and then you can bind a ReactiveValue
returned by useReactiveValue
to the component using model
propuseReactiveUndo
to undo/redo changes to a reactive statecreateReactiveStore
to create a reactive store and useReactiveStore
to use itproduce
to create a new stateuseReactive
and useReactiveValue
support nested objects and destructuring, common methods on arrays are also supported like push
, splice
and sort
state.count++
twice in a render will increment the count by 2, because when you call state.count++
the first time, the temporary state is updated, and then when getting the value of state.count
, the temporary state is used instead of the original state, so the second state.count++
will increment the temporary state instead of the original state. No confusing behavior like repeating setState(count + 1)
twice actually just increment the count by 1!npm install react-reactive-hooks
yarn add react-reactive-hooks
pnpm add react-reactive-hooks
useReactive
You can use useReactive
to create a reactive state, just like reactive
in Vue 3.
import { useReactive } from 'react-reactive-hooks';
const Counter: React.FC = () => {
const state = useReactive({
count: 0,
});
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => state.count++}>+</button>
<button onClick={() => state.count--}>-</button>
</div>
);
};
Nested objects are supported:
import { useReactive } from 'react-reactive-hooks';
const Todo: React.FC = () => {
const state = useReactive({
input: '',
nested: {
todos: [] as Array<{
text: string;
done: boolean;
}>,
},
});
const {
nested: { todos },
} = state;
return (
<>
<input
value={state.input}
onChange={(e) => (state.input = e.target.value)}
/>
<button
onClick={() => {
todos.push({
text: state.input,
done: false,
});
state.input = '';
}}>
Add
</button>
<ul>
{todos.map((todo, index) => (
<li key={index}>
<input
type="checkbox"
checked={todo.done}
onChange={(e) => (todo.done = e.target.checked)}
/>
<span>{todo.text}</span>
</li>
))}
</ul>
</>
);
};
As you can see in the proceeding example, common methods on arrays are supported like push
, splice
and sort
. Destructuring is also supported.
useReactive
uses a temporary state to trace the changes, so changes are triggered immediately. You can even write something like this:
const Counter: React.FC = () => {
const state = useReactive({
count: 0,
});
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => {
state.count++;
console.log(state.count); // 1 (if you use useState, this will be 0)
state.count++;
state.count++;
console.log(state.count); // 3 (if you use useState, this will still be 0)
}>+++</button>
</div>
);
};
WARNING: Symbols as keys are supported in
useReactive
, but they are currently not efficient enough. Due to the mechanism of the temporary state inuseReactive
, if you access a value on the state using a symbol as the key, likestate['foo'][Symbol('bar')]['baz']
, theget
trap of the proxy will fall back to be O(n) instead of O(1), where n is the number of changed properties with keys path containing one or more symbols. So if you have a lot of symbols as keys, you should consider splitting the state into multiple states.
WARNING: Avoid nesting too many objects or arrays in the state, because each time you access a property on the state, the
get
trap of the proxy will be called, and theget
trap will traverse the path of the property to find the value. For example,state.foo.bar.baz
will traversestate
->state.foo
->state.foo.bar
->state.foo.bar.baz
and create new proxies for each object in the path, that's why destructuring is supported. To avoid creating too many proxies and reduce the overhead of deep traversing, you should consider splitting the state into multiple states.
useReactiveValue
useReactiveValue
provides an alternative API to useReactive
for creating a simple reactive value. It's just like ref
in Vue 3.
import { useReactiveValue } from 'react-reactive-hooks';
const Counter: React.FC = () => {
const count = useReactiveValue(0);
return (
<div>
<p>Count: {count.value}</p>
<button onClick={() => count.value++}>+</button>
<button onClick={() => count.value--}>-</button>
</div>
);
};
Note that compared with ref
in Vue 3, you cannot use something like <p>Count: {count}</p>
to display the value, because count
is not a primitive value, it's an object with a value
property.
useReactiveUndo
useReactiveUndo
provides an easy way to undo/redo changes to a reactive state.
import { useReactiveUndo } from 'react-reactive-hooks';
const Counter: React.FC = () => {
const { state, undo, redo } = useReactiveUndo({
count: 0,
});
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => state.count++}>+</button>
<button onClick={() => state.count--}>-</button>
<button onClick={undo}>Undo</button>
<button onClick={redo}>Redo</button>
</div>
);
};
WARNING:
useReactiveUndo
deep clones the state when you callundo
orredo
to avoid some unexpected behaviors. So if you have a lot of data in the state, it may be slow.
reactive
Sometimes you may recall v-model
in Vue, which is a shorthand for value={state}
and onClick={(e) => setState(e.target.value)}
. reactive
provides a similar API.
import { Input } from '@chakra-ui/react';
import { useReactiveValue, reactive } from 'react-reactive-hooks';
const ReactiveInput = reactive(Input, ['value'], (model) => ({
onChange: (e) => {
model.value = e.target.value;
},
}));
// Use it in your component
const LoginPage: React.FC = () => {
const username = useReactiveValue('');
const password = useReactiveValue('');
return (
<div>
<ReactiveInput model={username} />
<ReactiveInput model={password} />
</div>
);
};
As you can see, the syntax is quite intuitive and easy to understand. reactive
takes a component, a list of props to be passed to the component, and a function to create the event handlers. The function takes a model
object as the only argument, and you can use it to set the value of the model.
In this case, we use a Input
component from Chakra UI, and we want to make the value
prop reactive. So we pass ['value']
as the second argument to reactive
(As you can see, it takes an array, so you can pass multiple prop names if you want to bind the ReactiveValue to multiple props). Then we create the onChange
event handler in the third argument, it is just the same as the onChange
event handler of the Input
component, and the model
object is just the ReactiveValue
we passed to the model
prop of ReactiveInput
.
Of course, you can use any event handler available on the component, in this case we use onChange
on Input
, but you can also use onClick
on Button
, onSubmit
on form
, etc.
createReactiveStore
and useReactiveStore
createReactiveStore
creates a reactive store and useReactiveStore
uses it.
// globalStore.ts
import { createReactiveStore } from 'react-reactive-hooks';
const globalStore = createReactiveStore({
count: 0,
});
export default globalStore;
// App.tsx
import { ReactiveProvider } from 'react-reactive-hooks';
import globalStore from './globalStore';
import Counter from './Counter';
const App: React.FC = () => {
return (
<ReactiveProvider store={globalStore}>
<Counter />
</ReactiveProvider>
);
};
export default App;
// Counter.tsx
import { useReactiveStore } from 'react-reactive-hooks';
import globalStore from './globalStore';
const Counter: React.FC = () => {
const store = useReactiveStore(globalStore);
return (
<div>
<p>Count: {store.count}</p>
<button onClick={() => store.count++}>+</button>
<button onClick={() => store.count--}>-</button>
</div>
);
};
export default Counter;
FAQs
A set of React hooks for reactive programming
We found that react-reactive-hooks demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.