Design Patterns: Vue feels like React - TypeScript 🔥
Tags: react, vue, javascript, typescript
Published: 2019-11-30 13:04:07

<em>Cover photo by <a href="https://unsplash.com/photos/LTyNF6X34GI">Ricardo Gomez Angel</a> on <a href="https://unsplash.com/">Unsplash</a>.</em>

When you first want to learn about a front-end technology, you will be confused by the many choices of tools, for example React, Vue, Angular, Svelte, etc. Of course we will not know if we do not try one of these, certainly all of these technologies have their pros and cons.

But in this article, we will not discuss which is the best, but we will discuss about how react developers can easily master these two frameworks (React & Vue), with the same pattern.

So, this is a long journey! Prepare yourself! 😃

<hr>

<h2>Setting Up Project</h2>

First we have to do is set up the project, let's start by creating a directory structure first.

<strong>1. The root directory structure</strong>

There are containers and presentational folders in the components folder. The difference is, the presentational component focuses on the UI element, while the container component which regulates the logic / storing data part, which will be displayed in the component container.
<pre><code>
    ├── src
    | ├── assets
    | ├── components
    |   ├── container
    |   ├── presentational
    ├── redux
    | ├── action
    | ├── reducer
    ├─
</code></pre>
<em>You can freely set the directory structure that you like, this is my directory structure for creating projects</em>

<strong>2. Use jsx & typescript</strong>

So, let's start by installing some of the dependencies that are needed. We can do this with type the following command:

```shell
npm i --save-dev typescript babel-preset-vca-jsx
npm i --save-dev @babel/plugin-syntax-dynamic-import @babel/plugin-transform-runtime 
npm i --save-dev @babel/preset-typescript @types/webpack-env source-map-loader 
npm uninstall babel-plugin-transform-runtime 
```
<em>We need to uninstall this package <code>babel-plugin-transform-runtime</code>, because we have installed the latest version <code>@babel/plugin-transform-runtime</code></em>

And then, we have to set some additional config because some dependencies require a supported Babel version

<blockquote>
<p><strong>Note:</strong> I happen to use this boilerplate: <a target="_blank" href="https://vuejs-templates.github.io/webpack/">Vue Webpack Template</a> you can choose any boilerplate.</p>
</blockquote>

Update your babel core dan babel loader
```shell
npm i --save-dev babel-core@^7.0.0-0 babel-loader@^8.0.6 
npm i --save-dev @babel/core@^7.6.4 @babel/preset-env@^7.6.3 
``` 

After installing all dependencies, we have to set additional config on <code>.babelrc</code> open the file, then add config <a href="https://gist.github.com/alfinsuryaS/1deb295c5ab3ec51b322298960ea8b52" target="_blank">.babelrc</a> we also need to set the webpack loader <a href="https://gist.github.com/alfinsuryaS/43d09d7d4e2def9d5a4df0c7623c1870" target="_blank">webpack config</a>

<blockquote>
<p><strong>Note:</strong> For typescript loader, you can use <a href="https://github.com/s-panferov/awesome-typescript-loader" target="_blank">Awesome TypeScript Loader</a></p>
</blockquote>

And don't forget, you also need to add some config in<code>.eslintrc.js</code>

```js
rules: {
    'import/extensions': ['error', 'always', {
      jsx: 'never',
      ts: 'never',
      tsx: 'never'
    }],
}
```

And next, create new file <code>tsconfig.json</code> and follow this config <a href="https://gist.github.com/alfinsuryaS/ea24b0e729b7a23fcb9d0ea07b71780e" target="_blank">tsconfig.json</a>


<blockquote>
<p><strong>Note:</strong> If you wanna using <code>TSLint</code>, you can follow this step <a href="https://palantir.github.io/tslint/usage/configuration/">configuring TSLint</a></p>
</blockquote>


After all config has been added, hooray! it's time to replace all your project file extensions from <code>.jsx/.js</code> to <code>.tsx/.ts</code>


<strong>3. Installing additional dependencies</strong>
```shell
npm i --save @vue/composition-api vuejs-redux redux @types/redux 
```


<h2>Main Concept</h2>
As very popular front-end tools, both of these tools have the same features, such as two-way data-binding, templating, routing, components, dependency injection, and many more.

Similar but not the same, there are some differences between these two tools, namely in terms of writing syntax, rendering components, managing state and data. Therefore, in this section we will peel one by one how to implement the react pattern in vue.


<h4>Components and Props</h4>
Components are special types of instructions such as JavaScript functions that will be displayed as separate parts and can be reused.

<blockquote>Here I use <code>Vue.component</code> and <code>createComponent</code> <strong>(vue composition api)</strong> you can use both</blockquote>

In rendering a component, the two are very different. React defines components as classes or functions, while Vue defines components as objects.

```tsx
export default createComponent({
    name: 'ComponentProps',
    props: {
        name: String,
        authorName: Array as () => string[]
    },
    setup(props) {
        return () => (
            <div className="components-props">
                <h2>{props.name}</h2>
                <p>{props.authorName}</p>
            </div>
        )
    }
})
```
<em>We no longer need to use <code>template</code> again, just JSX like React </em>🙂

```tsx
render () {
  return (
      <ComponentProps 
         name="Your name here" 
         commentId={['Name1', 'Name2']} 
      />
  )
}
```

<h4>Conditional Rendering</h4>
Conditional rendering works the same way like conditions work in JavaScript, we can use ternary or conditional operator.

```tsx
export default createComponent({
    name: 'ConditionalRendering',
    props: {
        show: Boolean
    },
    setup(props) {
        return () => props.show ? <p>True Condition</p> : <p>False Condition</p>
    }
})
```
```tsx
render() {
   return <ConditionalRendering show={false}/>
}
```

<h4>Handling Events</h4>
In Vue JS, when handling events, vue gives us directions for using <code> v-on </code> directive to handle these events. Since we already use JSX, so we don't need that anymore, we can use the JSX attribute like in React :)

```tsx
export default createComponent({
    setup(props) {
        return () => (
            <button onClick={props.handleButtonClick}>
                Click Event
            </button>
        )
    },
    props: {
        handleButtonClick: Function as () => void
    }
})
```
```tsx
render () {
  return (
       <HandlingEvent 
          handleButtonClick={() => alert("Click event. This works!")} 
       />
  )
}
```


<h4>Children in JSX</h4>
Children is a component that is used to display anything that you include between the opening and closing tags when calling the component.

In order to access this component, we can use <code> slots</code> function to be used as a content distribution outlet.
```tsx
export default Vue.component('Children', {
    render() {
        return (
            <div className="children">
                {this.$slots.default}
            </div>
        )
    }
})
```

```tsx
render () {
  return (
     <div className='container'>
        <Children>
          {/* what is placed here is passed as children */}
        </Children>
     </div>
  )
}
```


<h2>Lifecycle & Hooks</h2>
Lifecycle is a method that regulates the stages of the lifecycle in a component, and has their respective uses

<ul>
<li><strong><code>setup</code></strong>: is called right after the initial props resolution when a component instance is created. Lifecycle-wise, it is called before the <code>beforeCreate</code> hook.
</li>
<li><strong><code>onBeforeMount</code></strong>functions that are executed before the rendering process is run.</li>
<li><strong><code>onMounted</code></strong>function that are called only once after the first rendering is done. Usually this function is used for performing any side-effect causing operations such as AJAX requests.</li>
<li><strong><code>onUnmounted</code></strong>function that are executed to eliminate or delete a component from DOM.</li>
</ul>

```tsx
import {
    createComponent,
    reactive as useState,
    onBeforeMount as componentWillMount,
    onMounted as componentDidMount,
    onUnmounted as componentWillUnmount
} from '@vue/composition-api';

const LifecycleHooks = createComponent({
    setup() {
        const state = useState<{ loading: boolean, users: object }>({
            loading: false,
            users: []
        })

        componentWillMount(() => {
            console.log("Component before mount")
        })

        componentDidMount(() => {
            const API_URL = 'https://jsonplaceholder.typicode.com/users'
            fetch(API_URL)
                .then(res => res.json() as Promise<any>)
                .then(data => {
                    state.users = data,
                        state.loading = !state.loading;
                })
                .catch((err: Error) => {
                    throw err
                })
            console.log("Component Mounted")
        });

        componentWillUnmount(() => {
            console.log("Component Will Unmount")
        })

        return () => (
            <div className="lifecycle-hooks">
                {state.loading ? JSON.stringify(state.users) : <span>Loading...</span>}
            </div>
        )
    }
})

export default LifecycleHooks
```
<em>Yes, I used <code> as ... </code> for importing the module, this is only the naming, so it looks the same as the method name in React</em>

<ul><li><strong><code>reactive</code></strong> function is the equivalent of Vue 2's <code>Vue.observable()</code> it will return a new object that looks exactly the same as obj, and returns a reactive proxy of the original.</li>
<li><strong><code>watch</code></strong> function expects a function. It tracks reactive variables inside, as the component does it for the template. When we modify a reactive variable used inside the passed function, the given function runs again.

</li>
</ul>


```tsx
import {
    createComponent,
    reactive as useState,
    watch as useEffect
} from '@vue/composition-api';

const LifecycleHooks = createComponent({
    setup() {
        const state = useState<{ count: number }>({
            count: 0
        })

        /* => Re-run it whenever the dependencies have changed */
        useEffect(() => state.count, (nextState, prevState) => {
            console.log(nextState, '<= this is nextState')
            console.log(prevState, '<= this is prevState');
        })

        return () => (
            <div className="lifecycle-hooks">
                <button onClick={() => state.count++}>
                    Update Value
                </button>
            </div>
        )
    }
})
```

<h2>Redux & Vue</h2>

Surely you must already know what is Redux ?, yes you're right! Redux is an Agnostic State Management Library framework for Javascript Apps. Not like Vuex, redux can be used in any framework.

Redux has 4 main concepts: <em>reducers</em>, <em>actions</em>, <em>action creators</em>, and <em>store</em>. In Redux states are immutable and pure functions. Here are some things to know more about redux in vue:

<h3>Actions</h3>
Actions are simple Javascript objects that represent payloads of information that send data from your application to your store. Actions have a type and an optional payload.

```ts
export const INCREMENT = 'INCREMENT'
export const DECREMENT = 'DECREMENT'
export const RESET = 'RESET'


export const increment = () => {
    return { 
        type: INCREMENT 
        // your payload here
    }
}

export const decrement = () => {
    return { 
        type: DECREMENT 
    }
}

export const reset = () => {
    return { 
        type: RESET 
    }
}
```

<h3>Reducers</h3>
Reducers specify how the application's state changes in response to actions sent to the store. Reducers can then be combined to one root reducer to manage all of your applications state. 
```ts
type Action = { type: 'INCREMENT' } | { type: 'DECREMENT' } | { type: 'RESET' };

const Counter = (state: number = 0, action: Action) => {
    switch (action.type) {
        case 'INCREMENT': {
            return state + 1;
        }
        case 'DECREMENT': {
            return state - 1;
        }
        case 'RESET': {
            return state
        }
        default: return state
    }
}

export default Counter
```


Using <code>combineReducers</code> to calls all reducers when dispatching an action in  one root reducer function. It's very useful:)

```ts
import { combineReducers } from 'redux'
import userReducer from './reducer/user.reducer'

export default combineReducers({
    user: userReducer
    // your another reducer here
})
``` 

<h3>Store</h3>
A <em>store</em> is a place where you store the state of your application. Store, holds the whole state tree of your application that refers to the object with a few methods on it together. There is only a single store in a Redux application.

```tsx
import Vue from 'vue'
import { createStore } from 'redux'

import Provider from 'vuejs-redux';
import RootReducer from './rootReducer'

const store = createStore(RootReducer);

export default Vue.component('Provider', {
    render() {
        return (
            <Provider 
                mapStateToProps={this.mapStateToProps} 
                mapDispatchToProps={this.mapDispatchToProps} 
                store={store}> 
                {this.$scopedSlots.default}
            </Provider>
        )
    },

    props: ['mapStateToProps', 'mapDispatchToProps'],

    components: {
        Provider
    }
})
```

We can also create a custom provider that receive mapStateToProps and mapDispatchToProps as props and importing the store and passing it to every <code>Provider</code>.

```tsx
import Vue from 'vue';
import ContextConsumer from './redux';
import * as actions from './redux/action/user.action';

import ComponentContainer from './components/container/component-wrap';

export default Vue.component('App', {
  render() {
   return (
      <ContextConsumer 
          mapStateToProps={this.mapStateToProps} 
          mapDispatchToProps={this.mapDispatchToProps}>
            {({ incrementAction, userData }) => (
                <ComponentContainer>
                    <SingleComponent
                      value={userData.user}
                      handleClick={incrementAction} 
                    />
                </ComponentContainer>
            )}
      </ContextConsumer>
    )
  },

  components: {
    ContextConsumer
  },

  methods: {
    mapStateToProps(state) {
      return {
        userData: state
      }
    },
    mapDispatchToProps(dispatch) {
      return {
        incrementAction: () => dispatch(actions.increment())
      }
    }
  }
})
```

<h2>Higher-Order Components</h2>
A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API. They are a pattern that emerges from React’s compositional nature.

If you understand the concept of higher-order functions (HOF), of course it will be very easy to make HOC, because HOC is an implementation of HOF :)


```ts
import Vue from 'vue'

const useDataFetchingHOC = (WrappedComponent: JSX.IntrinsicElements) => (urlParam: string) => {
    return Vue.component('HOCFetch', {
        data: () => ({
            fetchData: null
        }),
        mounted: function() {
            fetch(urlParam)
                .then(response => {
                    if (!response.ok) { throw new Error(response.statusText) }
                    return response.json() as Promise<any>;
                })
                .then(data => this.fetchData = data)
                .catch((err: Error) => {
                    throw err
                })
        },

        render(createElement) {
            return !this.fetchData ? createElement('span', 'Loading Fetch...') :
                createElement(WrappedComponent, {
                    attrs: this.$attrs,
                    props: this.$props,
                    on: this.$listeners
            })
        }
    })
};

export default useDataFetchingHOC
```

```tsx
import { createComponent } from '@vue/composition-api'
import useDataFetchingHOC from '../presentational/hoc-component'

const dataSourceUrl = "https://jsonplaceholder.typicode.com/users";

const ContentSite = createComponent({
    setup() {
      return () => (
        <div className="content">
          <p>Yes, i'm in HOC</p>
        </div>
      )
    }
  })

export default useDataFetchingHOC(ContentSite)(dataSourceUrl)

```


<h2>Thanks For Reading</h2>
Thanks for reading, i hope you have enjoyed this article, and that it gave you some sort of inspiration for your own work. To be sure, Vue and React are very cool front-end tools, and are in great demand by many users. So, keep trying and learning new things, and don't forget always trust yourself! 😎

The complete source code for this project is available at <a href="https://gitlab.com/alfinsuryaS/react-design-pattern-in-vue">Gitlab</a>.