Categories
React Tips

React Tips — Function Props, Query Strings, and Default Props

React is a popular library for creating web apps and mobile apps.

In this article, we’ll look at some tips for writing better React apps.

Listen to State Changes in React

We can listen to state changes to React in several ways.

In class components, we can use the componentDidUpdate hook to listen for state and prop changes.

We can write:

componentDidUpdate(object prevProps, object prevState)

There’s also the componentWillUpdate method which is deprecated.

With function components, we can use the useEffect hook to watch for changes.

For instance, we can write:

const [name, setName] = useState('foo');
useEffect(getSearchResults, [name])

We pass in an array with the variables that we want to watch in the 2nd argument of the useEffect hook.

The first argument is a callback that’s called when name changes.

Pass Props in Link with React Router

We can pass in a query string to the Link .

For instance, we can write:

<Link to={{ pathname: `/foo/bar`, query: { backUrl } }} />

We set the query property with an object.

The object’s keys and values are transformed into a query string that’s attached after the pathname .

We can also take URL parameters with placeholders:

<Route name="/foo/:value" handler={CreateIdeaView} />

Then we can get it by using:

this.props.match.params.value

To get the query string, we can write:

this.props.location.search

to get the value.

Then we can use the URLSearchParams constructor to parse it and the values in it:

new URLSearchParams(this.props.location.search).get("foo")

If we have a query string ?foo=bar , then that will return 'bar' .

Call Parent Method with React

We can call a parent component’s method if we pass a function from the parent to the child as a prop.

For instance, we can write:

import React, {Component} from 'react';

class Child extends Component {
  render () {
    return (
      <div>
        <button
          onClick={() => this.props.someFunc('foo')}
        >
          click me
        </button>
      </div>
    )
  }
}

class Parent extends Component {
  someFunc(value) {
    console.log(value)
  }

  render() {
    return (
      <div>
        <Child
          someFunc={this.someFunc.bind(this)}
        />
      </div>
    )
  }

}

We pass the function from parent to child with the someFunc prop in the Parent component.

Then in the Child component, we call the function with this.props.someFunc .

Using function components, we can write:

import React from "react";

function Child(props){
  return(
    <>
      <button onClick={props.parentFunc}>
        click me
      </button>
    </>
  );
}

function Parent(){
  const parentFunc = () => {
    console.log("parent func called");
  }

  return (
    <>
      <Child
        parentFunc={parentFunc}
      />
    </>
  );
}

We have the Parent component which has the parentFunc that we pass to the Child component as a prop.

Then we called it by calling props.parentFunc when we click on the button in the Child component.

Import and Export Components Using React, ES6 and Webpack

To export components in a React project, we should export it as a default export.

For instance, we can write:

SomeNavBar.js

import React from 'react';
import Navbar from 'react-bootstrap/lib/Navbar';

export default class SomeNavBar extends React.Component {
  render(){
    return (
      <Navbar className="navbar-dark" fluid>
        {//...}
      </Navbar>
    );
  }
}

Then we import it with whatever name we want.

For instance, we can write:

import SomeNavBar from './SomeNavBar';

to import the navbar component.

We don’t put curly braces in default exports.

Set component Default Props on React Component

To set default props on a React component, we can use the prop-types package.

We’ve to install it by running:

npm i prop-types

Then we can write:

import PropTypes from 'prop-types';

class Address extends React.Component {
  render() {
    const { street, city } = this.props;
    <p>{street}, {city}</p>
  }
}

Address.defaultProps = {
  street: 'street',
  city: 'city',
};

Address.propTypes = {
  street: PropTypes.string.isRequired,
  city: PropTypes.string.isRequired
}

We set the defaultProps property to set the default prop values.

The object has the prop names as the keys and the default values as the values.

propTypes has the prop types for each prop.

We set them both to string and make them required with isRequired .

Conclusion

We can set default props and validate their format with the prop-types package.

We listen to state changes with class component lifecycle methods or the useEffect hook for function components.

Also, we can get and set query strings and URL parameters with React Router.

We can pass functions as props from parent to child.

Categories
Vue Tips

Vue Tips — DOM Elements, Vue Router, and Dynamic Components

Vue.js is a popular framework for creating front end web apps.

In this article, we’ll look at some tips for writing better Vue.js apps.

Open a Link in a New Tab with Vue Router

We can open a link in a new tab with Vue Router.

For instance, we can write:

const routeData = this.$router.resolve({ name: 'foo', query: { data: "bar" }});
window.open(routeData.href, '_blank');

We call the this.$router.resolve method to get the URL to open.

Then we call window.open to open the given URL.

The first argument is the URL.

'_blank' indicates that we open the URL in a new tab.

Run Vue.js Dev Server with HTTPS

We can change the Vue dev server’s config to serve the project over HTTPS rather than HTTP.

To do that, we read the private key and certificate.

And we set the URL to serve the project on.

For instance, we can write:

const fs = require('fs')

module.exports = {
  devServer: {
    https: {
      key: fs.readFileSync('./certs/key.pem'),
      cert: fs.readFileSync('./certs/cert.pem'),
    },
    public: 'https://localhost:8888/'
  }
}

We read in the files and set them as the properties of the https property.

To make a certificate, we can use the mkcert program to do it.

We can install it on Windows, Mac OS, or Linux by following the instructions on https://github.com/FiloSottile/mkcert.

Then we can create a new key and certificate by running:

mkcert -install

and:

mkcert example.com "*.example.com" example.test localhost 127.0.0.1 ::1

Then we created a certificate valid for localhost.

We just copy the files to the cert folder and rename them to match what we have in the config.

Then we can run npm run dev and serve the project.

Rerun Vue Component mounted() Method

To rerun the code that we wrote in the mounted method, we can move the code into its own method.

Then we can call that in mounted or anywhere else.

For instance, we can write:

new Vue({
  methods: {
    init(){
      //...
    }
  },
  mounted(){
    this.init();
  }
})

Then we can write:

<button @click="init">reset</button>

We set the init method as the click handler and also run it in the mounted hook.

Now we can reuse it as we wish.

Trigger Events Using Vue

We can set a ref on an element and then trigger the event on the element.

For instance, we can write:

<template>
  <button type="button" @click="onClick" ref="aButton">
    click me
  </button>
</template>

<script>
export default {
  methods: {
    onClick($event) {
      const elem = this.$refs.aButton.$el;
      elem.click();
    }
  }
}
</script>

We have a button that we assigned a ref to.

Then we get the button’s ref’s element in the onClick method.

It returns the DOM node for the button.

Then we call click to trigger a click on the button DOM node.

Share a Method Between Components in Vue

To share a method between components in Vue, we can create a module that exports the method.

For instance, we can write:

src/shared.js

export default {
  bar() {
    alert("bar")
  }
}

Then we can use it in our component by writing:

src/App.js

<template>...</template>

<script>
import shared from './shared'

export default {
  created() {
    shared.bar();
  }
}
</script>

We imported the shared module and called the bar method inside it.

Alternatively, we can create a mixin, which is a piece of code that can be merged into our component.

For instance, we can write:

const cartMixin = {
  methods: {
    addToCart(product) {
      this.cart.push(product);
    }
  }
};

Then we can use it in our component by writing:

const Store = Vue.extend({
  template: '#app',
  mixins: [cartMixin],
  data(){
    return {
      cart: []
    }
  }
})

We call Vue.extend to create a subclass of the Vue constructor.

We can then make a new instance of Store and render that in our app.

Pass a Component as Props and Use it in a Child Component in Vue

We can pass in the component’s tag name into the is prop of the component component.

For instance, we can write:

<template>
  <div>
    <component :is="childComponent">foo bar</component>
  </div>
</template>

childComponent is the string with the tag name of the component.

The stuff between the tags is the content that’s filled into the default slot.

Conclusion

We can create a shared module or a mixin to create shared code in Vue.

Also, we can use this.$router.resolve to get the path that we can use elsewhere.

Vue projects can be served with HTTPS.

We can get the HTML element’s DOM object with refs.

Categories
Vue Tips

Vue Tips — Login, GET Requests, and Serve Vue Project via HTTPS

Vue.js is a popular framework for creating front end web apps.

In this article, we’ll look at some tips for writing better Vue.js apps.

Force Download with GET Request using Axios

We can make the response always download by passing our response data into the Blob constructor.

For instance, we can write:

axios
  .get(`download-pdf`, {
    responseType: 'arraybuffer'
  })
  .then(response => {
    const blob = new Blob(
      [response.data],
      { type: 'application/pdf' }
    ),
    const url = window.URL.createObjectURL(blob);
    window.open(url) ;
  })

We make a GET request to the download-pdf endpoint to download our PDF.

We make sure that we specify that the responseType is 'arraybuffer' to indicate that it’s a binary file.

Then in the then callback, we get the response parameter, which has the data we want in the data property.

We pass that into the Blob constructor in an array.

And we specify the MIME type of the response data.

Then we create the URL that lets us download the file with createObjectURL .

Finally, we call window.open with the url to download the file.

We can also specify the file name of the downloaded file by making a small change in the then callback.

To do that, we write:

axios
  .get(`download-pdf`, {
    responseType: 'arraybuffer'
  })
  .then(response => {
    const blob = new Blob(
      [response.data],
      { type: 'application/pdf' }
    ),
    const link = document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    link.download = "file.pdf";
    link.click();
  })

Instead of creating an object URL directly, we create an invisible link first and set the file name as the download property of it.

Then we call click to download it.

Fire an Event When v-model Changes

We can add a change event listener to our input to listen for input value changes.

For instance, we can write:

<input
  type="radio"
  name="option"
  value=""
  v-model="status"
  v-on:change="onChange"
>

onChange is the name of the change event handler.

We can also replace v-on: with @ for short:

<input
  type="radio"
  name="option"
  value=""
  v-model="status"
  @change="onChange"
>

Also, we can add a watcher for our model property instead of attach a change event listener.

For instance, we can write:

new Vue({
  el: "#app",
  data: {
    status: ''
  },
  watch: {
    status(val, oldVal) {
      console.log(val, oldVal)
    }
  }
});

With the watch property, we added a watcher for the status variable as indicated by the name.

val has the current value and oldVal has the old value.

Redirect to Requested Page After Login Using Vue Router

We can add a redirect path to the query parameter when redirecting to the login page.

For instance, we can write:

onClick() {
  if (!isAuthenticated) {
    this.$router.push({ name: 'login', query: { redirect: '/profile' } });
  }
}

We check for authentication credentials.

If they aren’t present, then we call this.$router.push to redirect to the route for the login page.

The query property has the path to redirect to when login is successful.

We have a query string with the redirect key and the /profile value.

Then in our login form component, we can write:

submitForm() {
  login(this.credentials)
    .then(() => this.$router.push(this.$route.query.redirect || '/'))
    .catch(error => {
       //...
    })
}

in the methods property.

We call login which returns a promise.

So we can call then with a callback to redirect to the path in the query string.

We get that path with the this.$route.query.redirect property.

Then we call this.$router.push to do the redirect.

In case it’s not defined, we redirect to the / route.

Run Vue.js Dev Server with HTTPS

We can run Vue’s dev server with HTTPS by changing the code in build/dev-server.js to read the certificate and the private key files.

Then we can use them to call https.createServer to create the HTTPS server.

For instance, we can write:

const https = require('https');
const fs = require('fs');
const options = {
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem'))
};
const server = https.createServer(options, app).listen(port);

We read the files and put them into the options object.

Then we pass the object into the https.createServer method.

app is the Express server’s app object.

Conclusion

We can serve a Vue project with a dev HTTPS server.

Also, we can download files with Axios.

And we can add watchers to watch for variable changes.

Categories
Vue Tips

Vue Tips — Slots, Vue Router, and Mutations

Vue.js is a popular framework for creating front end web apps.

In this article, we’ll look at some tips for writing better Vue.js apps.

How to Pass Down Slots Inside Wrapper Component

To pass down slots in a wrapper component, we can loop through all the slots and pass them down to the child.

For instance, we can write:

<wrapper>
  <parent-table v-bind="$attrs" v-on="$listeners">

    <slot v-for="slot in Object.keys($slots)" :name="slot" :slot="slot"/>

    <template v-for="slot in Object.keys($scopedSlots)" :slot="slot" slot-scope="scope">
      <slot :name="slot" v-bind="scope"/>
    </template>

  </parent-table>
</wrapper>

We get the slots with the $slots variable.

Then we use Object.keys to get names of the slots so that we can loop through all of them and pass the name down.

Likewise, we can loop through the scoped slots with the $scopedSlots variables.

We get the keys the same way and loop through them with v-for the same way.

With Vue 2.6, the v-slot= directive is introduced to let is pass the slots down.

For instance, we can write:

<wrapper>
  <parent-table v-bind="$attrs" v-on="$listeners">
    <template v-for="(_, slot) of $scopedSlots" v-slot:[slot]="scope">
      <slot :name="slot" v-bind="scope"/>
    </template>
  </parent-table>
</wrapper>

We loop through the slot with v-for .

We get the scoped slots with the $scopedSlots variable.

slot is the slot name again.

This time, we pass it to the v-slot directive as a modifier to pass down the named slot.

scope has the scope from the scoped slot.

We use v-bind to get the scope.

And we

Alternatively, we can use render function to pass down the slots.

For instance, we can write:

render(h) {
  const children = Object.keys(this.$slots)
    .map(slot => h('template', { slot }, this.$slots[slot]))

  return h('wrapper', [
    h('parent-table', {
      attrs: this.$attrs,
      on: this.$listeners,
      scopedSlots: this.$scopedSlots,
    }, children)
  ])
}

We get the slots with the this.$slots property.

We call Object.keys to get the slot names.

And we call map on it to map the slot names to the template components.

And we pass in the slots name and the scope down,.

Then we return a wrapper component with the parent-table component with the listeners, attributes, and scoped slots and children as the children.

Get Query Parameters from a URL in Vue.js

We can get the query parameters from a URL in Vue with the this.$route.query property.

To get the query parameter foo=bar , we write:

this.$route.query.foo

and we get 'bar' as the value.

This is available assuming we’re using Vue Router in our Vue app.

If we haven’t added it, we can write:

index.html

<script src="https://unpkg.com/vue-router"></script>

index.js

const router = new VueRouter({
  mode: 'history',
  routes: [
    {
      path: '/page',
      name: 'page',
      component: PageComponent
    }
  ]
});

const vm = new Vue({
  router,
  el: '#app',
  mounted() {
    const q = this.$route.query.q;
    console.log(q)
  },
});

to get it.

We create the VueRouter instance and pass it into the object we passed into the Vue constructor.

routes has the routes.

Passing Multiple Parameters to a Mutation with Vuex

To pass multiple parameters to action with Vuex, we can pass in an object as the payload.

For instance, we can create our mutation by writing:

mutations: {
  setToken(state, { token, expiration }) {
    localStorage.setItem('token', token);
    localStorage.setItem('expiration', expiration);
  }
}

We have an object as the second parameter.

It has the token and expiration properties.

Then we can invoke the mutation by writing:

store.commit('setToken', {
  token,
  expiration,
});

We invoke the setToken mutation with the token and expiration properties in an object as the 2nd argument.

Reload Route with Vue Router

To reload a route with Vue Route, we can call the this.$router.go() method.

If it has no arguments, then it’ll reload the current route.

We can also add an unique value for the key prop to the router view:

<router-view :key="$route.fullPath"></router-view>

This way, it’ll notice when the path changes and it’ll trigger a reload of the component with new data.

Conclusion

We can reload our route with this.$router.go() .

There are many ways to pass a scope down to a child.

We can get query parameters in our component if we use Vue Router.

To pass in multiple pieces of data into a mutation, we can pass in an object with all the data we want into the mutation.

Categories
Vue Tips

Vue Tips — Making Components Play Nice with Each Other

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how Vue components can play nice with each other.

Assign Attributes to the Right Elements

We can pass attributes from a grandparent component to a grandchild component by setting the inheritAttrs option to false , and then add v-bind='$attrs' to the child of the grandparent to pass the attributes of the grandparent to the grandchild.

For instance, we can write the following code to do that:

index.js :

Vue.component("foo", {
  inheritAttrs: false,
  template: `
    <p v-bind='$attrs'>foo</p>
  `
});

new Vue({
  el: "#app"
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <foo class="foo"></foo>
    </div>
    <script src="index.js"></script>
  </body>
</html>

Then the foo class will be applied to the p element in foo since we set inheritAttrs to false in foo .

Use Browser Norms for Keyboard Navigation

We should follow the conventions for keyboard shortcuts for common operations if we’re going to handle keyboard shortcuts.

For instance, the tab key lets us move to different form fields. Enter is for submitting data, etc.

Use Events Over Callbacks

Emitting events from child to parent is better than callback functions that are passed in from parent to child.

For instance, we have to pass callbacks as props the following way:

index.js :

Vue.component("foo", {
  props: ["showAlert"],
  methods: {
    displayAlert() {
      if (typeof this.showAlert === "function") {
        this.showAlert("foo");
      }
    }
  },
  template: `
    <button @click='displayAlert'>Click Me</button>
  `
});

new Vue({
  el: "#app",
  methods: {
    showAlert(text) {
      alert(text);
    }
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <foo :show-alert="showAlert"></foo>
    </div>
    <script src="index.js"></script>
  </body>
</html>

In the code above, we have the show-alert prop, which we pass the showAlert method from the root instance to the child. This is more complex and error-prone because we have to make sure that we passed in a function as the value of the prop.

We have to check that by using typeof of Vue’s built-in prop type validation.

Then we have to call the function that’s passed in as the value of the prop as we did in the displayAlert function. The name is different so it won’t clash with the showAlert prop.

On the other hand, if we emit events, then we won’t have to pass functions from parent to child to run something in the parent when something happens in the child.

For example, we can rewrite the example above by writing:

index.js :

Vue.component("foo", {
  template: `
    <button @click='$emit("button-clicked", "foo")'>Click Me</button>
  `
});

new Vue({
  el: "#app",
  methods: {
    showAlert(text) {
      alert(text);
    }
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <foo @button-clicked="showAlert($event)"></foo>
    </div>
    <script src="index.js"></script>
  </body>
</html>

The code above is a lot simpler. We just call $emit with the event name as the first argument and the payload that we want to send to the parent as the second.

Then we can listen to the button-clicked event from the parent as we did in index.html by writing:

@button-clicked="showAlert($event)"

Then $event object has the payload, which is 'foo’, so we can pass it straight into the showAlert method. Finally, we’ll see the alert display when we click the button.

Limit In-Component Styles

Scoped in-component styles in single-file components are only applied to the component itself. If we’re reusing it in many locations or apps, then we should let the styles be in that app instead so that we won’t have conflicts of styles between our component and the styles that’s already in the app.

This makes styling reusable components hard since we’ve to deal with conflicts.

Therefore, we should either leave the styles out of the single-file component or have the ability to toggle them on and off.

For instance, we can toggle the styles with an is-styled prop as follows:

HelloWorld.vue :

<template>
  <div>
    <p :class="{'is-styled': isStyled}">{{ msg }}</p>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
    isStyled: Boolean
  }
};
</script>

<style scoped>
.is-styled {
  color: #42b983;
}
</style>

App.vue :

<template>
  <div id="app">
    <button @click="isStyled = !isStyled">Toggle Style</button>
    <HelloWorld msg="Hello Vue" :is-styled="isStyled"/>
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld";

export default {
  name: "App",
  components: {
    HelloWorld
  },
  data() {
    return {
      isStyled: true
    };
  }
};
</script>

In the code above, we have :class to bind the class dynamically according to the value of the isStyled prop that we passed in in HelloWorld.vue

Then in App.vue , we can set the isStyled value and pass it into the is-styled prop as the value so that we can have the ability to turn our HelloWorld ‘s component’s styles on and off as we wish.

Conclusion

We can use v-bind='$attrs' to inherit attributes from parent to child. If we’re going to add keyboard shortcuts handling to our app, we should following the conventions of other apps to make using our app convenient.

Also, we should emit events instead of calling callbacks passed from parent to child.

Finally, if we have shared components, we should either leave out the styles or have the ability to toggle them on and off.