React Hooks’ popularity has soared since the API was first released in v16.8. And for good reason — the ability to write and compose components as mere functions gives you incredible power and flexibility. Together with terrific TypeScript support, it’s no surprise Hooks are so appealing.
Then came the Vue Composition API, with its beautiful reactivity system and improved static typing support. It is indeed a good time to be a frontend engineer, but decision paralysis can be a real struggle. Do I give up writing Vue and port to React Hooks, or vice-versa?
Wait! What if you didn’t have to make a choice? What if you could use the Vue Composition API inside your React components? Here comes reactivue
to enable you to do just that.
reactivue
?reactivue
describes itself as the best elements from React and the best elements from Vue.js in one framework. In a nutshell, you can write Vue snippets inside React — think best of both worlds.
Note:
reactivue
is currently still experimental and might have some breaking changes in the future. Don’t use it in production yet, but feel free to try it out.
This article assumes that you have a fair understanding of Vue’s reactivity with the Composition API as well as React functional components with Hooks. You do not need to be an expert by any means, but a fundamental familiarity of the concepts is necessary. We won’t be getting into the details of how they work, but how they work together with the help of reactivue
.
Without further ado, let’s install reactivue
. Installation is simple — just add the package with Yarn or npm, as shown below:
npm i reactivue // or yarn add reactivue
Let’s build a simple color picker to see this in action. To keep things fairly straightforward, we’ll do away with styling and just stick to understanding how the setup works. The interesting part is visualizing how reactivue
integrates the Vue Composition API with React functions to make them work in tandem seamlessly.
Our color picker merely picks a color from an HTML color input and updates the background color of our div based on the selected color. As an assignment, you could extend it to set various styles or add complex conditional logic to better understand the concepts. The code is below, followed by a deep dive.
import * as React from "react"; import { useState, ChangeEvent } from "react"; import { defineComponent, ref, computed, onUnmounted, onMounted } from "reactivue"; interface Props { color: string; height: number; width: number; } const MyColor = defineComponent( // setup function in Vue ({ color, width, height }: Props) => { const background = ref(color); const boxWidth = ref(width); const boxHeight = ref(height); const boxStyle: any = computed(() => { return { width: `${boxWidth.value}px`, height: `${boxHeight.value}px`, backgroundColor: background.value }; }); onMounted(() => console.log("Hello World")); onUnmounted(() => console.log("Goodbye World")); return { background, boxWidth, boxStyle, boxHeight }; }, // functional component in React ({ background, boxWidth, boxStyle }) => { // you are now in react territory. You can use all hooks and methods. const [newStyle, setNewStyle] = useState(boxStyle); const onChangeColor = ({ target: { value } }: ChangeEvent<HTMLInputElement>) => { setNewStyle({ ...boxStyle, backgroundColor: value }); }; return ( <div> <input type="color" onChange={(e) => onChangeColor(e)} style={{ marginBottom: "20px", width: "300px" }} /> <div style={newStyle}></div> </div> ); } ); // use it as you normally would render(<MyColor color="red" width={300} height={200} >, el)
A working example is below:
This is fairly simple to follow. We have imported both React and the methods necessary for our setup from reactivue
.
Here, you are able to create a so-called factory component that imports the reactivity APIs from @vue/reactivity
. In that sense, the lines bellow are equivalent:
// both line are equivalent. import { ref, reactive, computed } from 'reactivue' import { ref, reactive, computed } from '@vue/reactivity
The defineComponent
function exposes the lifecycle hooks from Vue. We create a Props
object, which would be passed to the React components. Using the Composition API , we are able to set up the internal state with ref
as well as create a computed property that updates the style inside our React component.
I won’t go into detail regarding how the Composition API works. All we need to understand is that the background
, boxWidth
, boxHeight
, and boxStyle
are all reactive and passed off to the React components, where they can be used and transformed as they would in any React component.
In our case, we are setting a newStyle
state using React’s useState
Hook. We accept the background props and update it inside the React component with a new state.
Here, you can already see how we pass off a state from Vue and change it inside React. It’s then recomputed through both Vue’s reactivity (from number
to px
) and in React, which sets a new style.
This may appear confusing at first, but it is surprisingly simple. Play with the code and try to track changes to different styles or add some other computed properties.
You can use it as a Hook as well.
The defineComponent
factory is actually syntactic sugar to useSetup
. The below code is similar to the above using the component factory.
import * as React from "react"; import { useState, ChangeEvent } from "react"; import { useSetup, ref, computed } from "reactivue"; function MyColor(Props: Props) { const state = useSetup( ({ color, width, height }: Props) => { // shortened for brevity; same as above example const boxStyle: any = computed(() => { return { width: `${boxWidth.value}px`, height: `${boxHeight.value}px`, backgroundColor: background.value }; }); return { background, boxWidth, boxStyle, boxHeight }; }, Props // pass React props to it ); // state is a plain object just like React state const { boxStyle } = state; const [newStyle, setNewStyle] = useState(boxStyle); const onChangeColor = ()=> { //shortened for brevity} return (<div> /**/ </div); }
The major difference is that you access the states from Vue as a plain state object in React. You could simply destructure what you need from the state and use it inside React functional components.
Another important concept you probably love with React Hooks is composing and reusing composition logic, otherwise known as custom Hooks. You can achieve this with the Hook factory in reactivue
.
Let’s say you’d like to write a custom Hook for computing the average of a given array of numbers and increment the numbers in the array by the input value. Let’s write a useCalculation
Hook.
import { createSetup, ref, computed, onUnmounted, onMounted } from "reactivue"; export interface Props { numbers: Array<number>; } // create a custom hook that can be reused export const useCalculation = createSetup((props: Props) => { const numbers = ref(props.numbers); const average = computed( () => numbers.value.reduce((a, b) => a + b) / numbers.value.length ); const increment = (by: number) => { return numbers.value.map((num) => { return num + by; }); }; onMounted(() => console.log("Hello World")); onUnmounted(() => console.log("Goodbye World")); return { numbers, average, increment }; }); export default useCalculation;
Then this piece of logic can be reused as below;
import * as React from "react"; import { ChangeEvent, useState } from "react"; import { Props, useCalculation } from "./useCalculation"; export const App = (props: Props) => { const [myNumbers, setMyNumbers] = useState(props.numbers); const { increment, average } = useCalculation({ numbers: myNumbers }); const onChange = ({ target: { value } }: ChangeEvent<HTMLInputElement>) => { setMyNumbers(increment(parseInt(value,10))); }; return ( <div> <input type="number" onChange={(e) => onChange(e)} /> <ul> {" "} {myNumbers.map((num, index) => { return <li key={index}>{num}</li>; })} </ul> <br /> <div>The Average is of numbers = {average} </div> </div> ); };
A working demo is below:
reactivue
implements Vue’s basic lifecycle hooks then binds them with React’s lifecycles hooks. For some lifecycles that don’t have the React equivalent, they will be called somewhere near when they should be called (for example, onMounted
will be called right after onCreated
).
They can be used most of the time like you would in Vue.
defineComponent()
– accepts a setup function and a render function that will return a React functional componentuseSetup()
– resolves the Composition API’s setupcreateSetup()
– a factory to wrap your logic into reusable custom HooksHere we have explored the interesting possibility of mixing and matching concepts from two powerful tools into one. Even though this is still very much experimental, we can see the great benefits we stand to enjoy — the simplicity of Vue and its reactivity system and the best parts of React.
We looked at setting up reactive data and computed properties in Vue and then how we can work with them in React via props or state objects. We have looked at this through easy-to-grasp examples and small applications to appreciate the possibilities. You can take this a step further by forking the examples and building awesome projects. This would be the best Reaction from my point of Vue 😃.
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.