Olasunkanmi John Ajiboye TypeScript and Rust enthusiast. Writes code for humans. From the land of Promise.

Using Vue Composition API in React functional components

5 min read 1460

Using Vue Composition API in React Functional Components

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.

What is 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.

Getting started

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

Usage

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:

We made a custom demo for .
No really. Click here to check it out.

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.

Hooks

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.

Hook factory

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:

Extra APIs

Lifecycles

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 component
  • useSetup() – resolves the Composition API’s setup
  • createSetup() – a factory to wrap your logic into reusable custom Hooks

Conclusion

Here 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 😃.

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

Which of these topics are you most interested in?
ReactVueAngularNew frameworks
Do you spend a lot of time reproducing errors in your apps?
YesNo
Which, if any, do you think would help you reproduce errors more effectively?
A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

Experience your Vue apps exactly how a user does

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. https://logrocket.com/signup/

LogRocket is like a DVR for web 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 - .

Olasunkanmi John Ajiboye TypeScript and Rust enthusiast. Writes code for humans. From the land of Promise.

Leave a Reply