Categories
React Tips

React Tips — Test Buttons, Form Validation, and 404 Routes

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.

Simulate a Button Click in Jest

To simulate a button click with Jest, we can write:

import React from 'react';
import { shallow } from 'enzyme';
import Button from './Button';

describe('Test button', () => {
  it('Test click event', () => {
    const mockOnClick = jest.fn();
    const button = shallow((<Button onClick={mockOnClick}>click me</Button>));
    button.find('button').simulate('click');
    expect(mockOnClick.mock.calls.length).toEqual(1);
  });
});

We create a mock function with and pass it into our button component.

Then we get the button and call simulate with the argument 'click' do simulate a click.

Then we can check if the mockOnClick function is called at the end.

Alternatively, we can write similar code in a Sinon test.

For instance, we can write:

import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';

import Button from './Button';

describe('Test Button component', () => {
  it('simulates click events', () => {
    const mockOnClick = sinon.spy();
    const button = shallow((<Button onClick={mockOnClick}>click me</Button>));

    button.find('button').simulate('click');
    expect(mockOnClick).toHaveProperty('callCount', 1);
  });
});

We create a mock function with sinon.spy() .

Then we pass that into the onClick callback.

And then we call simulate with the 'click' argument to simulate the click.

We can also call our onClick function from the props directly.

For example, we can write:

wrapper.find('Button').prop('onClick')()

How to Add Multiple Middlewares to Redux

We can add multiple middlewares to Redux with the applyMiddleware method.

For example, we can write:

const createStoreWithMiddleware = applyMiddleware(ReduxThunk, logger)(createStore);

We just pass them all into the applyMiddleware function directly.

Also, we can put them all into an array and spread them into applyMiddleware :

const middlewares = [ReduxThunk, logger];
applyMiddleware(...middlewares)

Callback When DOM is Loaded in React

componentDidMount is the callback that’s called when the DOM is loaded.

For instance, we can write:

class Comp1 extends React.Component {
  constructor(props) {
    super(props);
    this.handleLoad = this.handleLoad.bind(this);
  }

  componentDidMount() {
    window.addEventListener('load', this.handleLoad);
  }

  componentWillUnmount() {
    window.removeEventListener('load', this.handleLoad)
  }

  handleLoad() {
    //...
  }

  render() {
    //...
  }
}

We have the handleLoad method that we added as the listener fo the load event in componentDidMount .

In componentWillUnmount , we remove the event listener when the component unloads.

Properly Validate Input Values with React

To make form validation easy for us, we can use the react-hook-form library.

For instance, we can write:

import React from 'react'
import useForm from 'react-hook-form'

function App() {
  const { register, handleSubmit, errors } = useForm()
  const onSubmit = (data) => { console.log(data) }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstname" ref={register} />

      <input name="lastname" ref={register({ required: true })} />
      {errors.lastname && 'last name is required'} {

      <input name="age" ref={register({ pattern: /d+/ })} />
      {errors.age && 'invalid age.'}

      <input type="submit" />
    </form>
  )
}

We use the useForm hook that comes with the package.

Then we can use the functions and objects that are returned with the hook to check for valid input and display error messages.

register lets us register the input field with react-hook-form and add the validation rules.

Registering the field will make it available when we run our submit handler.

We can pass an object with the validation rules as we did in the last name and age inputs.

We applied the required rule with the last name field.

And we set a regex for the valid format in the age field.

In the onSubmit function, we have the data parameter which has the filled-in values.

It’s only run if all the fields are valid.

errors have errors in a field.

Add a Not Found Page with React Router

We can add a not found page with React Router by adding a Router compoent with no path.

For instance, we can write:

<Switch>
  <Route exact path="/">
    <Home />
  </Route>
  <Route path="/will-match">
    <WillMatch />
  </Route>
  <Route path="*">
     <NoMatch />
  </Route>
</Switch>

The last entry:

<Route path="*">
  <NoMatch />
</Route>

is the wildcard route that’s loaded when the URL doesn’t match anything else listed.

Conclusion

We can create a no matching route with React Router.

We can simulate a button click with a mock function.

To make form validation easy, we should use a package.

And we can add multiple middlewares with React-Redux.

Categories
React Tips

React Tips — SSR, Link Underline, and Authorization Header

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.

Server-Side Rendering of React Components

We can do server-side rendering with React and React Router.

For instance, we can write:

const Router = require('react-router');
const React = require("react");
const url = require("fast-url-parser");

const routeHandler = (req, res, next) => {
   const path = url.parse(req.url).pathname;
   if (/^/?api/i.test(path)) {
     return next();
   }
   Router.run(routes, path, (Handler, state) => {
     const markup = React.renderToString(<Handler routerState={state} />);
     const locals = { markup };
     res.render("main", locals);
  });
};

app.get('/', routeHandler);
app.listen(3000);

We call Router.run to route the routes for the front end to what we want.

In the Router.run callback, we run React.renderToString to render our Handler component to a static strung.

Then we pass it into the locals object and pass it off to the 'main' template.

Without React Router, we can write:

import React from 'react'
import ReactDOM from 'react-dom/server'
const express = require('express');
const app = express();

const Hello = (props) => {
  return (
    <div>
      <p>hello</p>
    </div>
  )
}

app.get('/', (req, res) => {
  const app = ReactDOM.renderToString(<Hello />)
  const html = `<!doctype html>
    <html>
      <head>${styles}</head>
      <body>
        <div id="root">${app}</div>
      </body>
    </html>`
  res.end(html)
})

app.listen(3000)

We use Express as we did in the previous example, but we removed the Reactv Router part.

We just call ReactDOM.renderToString to render the component to a string.

Then we render it straight as HTML.

In both examples, we can’t add anything dynamic to our components like event handlers or state changes to our components since it’s been rendered as a static string.

If we need all that, then we can use frameworks like Next.js to make adding dynamic features much easier.

Get Rid of Underline for Link Component of React Router

We can get rid of the underline for the Link component by changing some styles.

For instance, we can use styled-components and write:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';

const StyledLink = styled(Link)`
  text-decoration: none;
  &:focus, &:hover, &:visited, &:link, &:active {
    text-decoration: none;
  }
`;

export default (props) => <StyledLink {...props} />;

We pass in the Link component to the styled tag so that we can style it.

We applied text-decoration: none on the link and all its pseudoclasses to make remove the underline.

If we’re only styling one link, we can also write:

<Link style={{ textDecoration: 'none' }} to="/home">
  Home
</Link>

We pass in an object to the style tag to style it.

We also added textDecoration: 'none' to remove the underline.

Attach Authorization Header for All Axios Requests

If we’re using Axios in our React app, we can add an authorization header to all requests to using its request interceptor feature.

For instance, we can write:

axios.interceptors.request.use((config) => {
  const token = store.getState().token;
  config.headers.Authorization =  token;
  return config;
});

We get the header from a central store and then set that as the value of the Authorization header by setting the returned value as the value of the config.headers.Authorization property.

We can also create our own shared module that calls the Axios requests as follows:

import axios from 'axios';

const httpClient = (token) => {
  const defaultOptions = {
    headers: {
      Authorization: token,
    },
  };

  return {
    get: (url, options = {}) => axios.get(url, { ...defaultOptions, ...options }),
    post: (url, data, options = {}) => axios.post(url, data, { ...defaultOptions, ...options }),
    put: (url, data, options = {}) => axios.put(url, data, { ...defaultOptions, ...options }),
    delete: (url, options = {}) => axios.delete(url, { ...defaultOptions, ...options }),
  };
};

We have a client function that gets the token and returns an object with common HTTP request methods with the request options, which includes the token merged into the options.

Then we can reuse that anywhere:

const request = httpClient('secret');
request.get('http://example.com');

Conclusion

We can do server-side rendering with React, React Router and Express,

To get rid of the underline of a React Router link, we can pass in styles to remove it in various ways.

There are also multiple ways to pass a token to all routes into all Axios requests.

Categories
React Tips

React Tips — Scroll, Navigate, and Keeping Focus

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.

React Router Scroll to Top on Every Transition with Hooks

We can create a wrapper component to scroll a route component to the top whenever we navigate.

For instance, we can write:

import React, { useEffect, Fragment } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history, children }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return <Fragment>{children}</Fragment>;
}

export default withRouter(ScrollToTop);

We call history.listen to listen for navigation.

In the callback for it, we call window.scrollTo(0, 0) to scroll to the top.

It returns a function to clear the listener, so we call that in the function that we return in the useEffect callback.

We pass in an empty array to useEffect to attach the listener only once when the ScrollToTop component is mounted.

Then we can use it by writing;

<Router>
  <ScrollToTop>
    <Switch>
       <Route path="/" exact component={Home} />
    </Switch>
  </ScrollToTop>
</Router>

We wrap ScrollToTop around all our routes so that scrolling to top is done when any route component loads.

React Router and withRouter

withRouter is a component that we can call to let us get the location , history , and match objects as props in our component.

For instance, we can write:

import React from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";

class App extends React.Component {
  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  };

  render() {
    const { match, location, history } = this.props;
    return <div>{location.pathname}</div>;
  }
}

const ShowTheLocationWithRouter = withRouter(App);

match is an object that has information about a router’s path.

It has the params property to get the segments of the path.

isExact is true if the path matches the URL.

path is a string with the pattern that’s used to match.

url is a string that has the matched portion of the URL.

location is an object that has the pathname with the pathname.

search is the query string portion of the URL.

hash has the part of the URL after the # sign.

history is the native history object.

It has the length with the number of entries in the history stack.

The location property has the parts of the URL returned as an object.

It’s the same as the location object itself.

push is a method to let us navigate to a new path.

replace lets us navigate to a new path by overwriting history.

go navigate to a history entry by the number of steps.

goBack moves to the previous history.

goFoward lets us go to the path in the next history entry.

React Components from Array of Objects

We can render an array of objects into a list of component by using the map method.

For instance, we can write:

const Items = ({ items }) => (
  <>
    {items.map(item => (
      <div key={items.id}>{items.name}</div>
    ))}
  </>
);

All we do is call map with a callback to return the entries derived from the items entry.

key should have a unique ID so that React can distinguish each entry even if they’ve moved.

Fix Input Losing Focus When Rerendering Problem

This may happen if we create our own input component and we created it within the render method.

This will cause a new instance of our custom input component to be created every time the input is rendered.

Therefore, we should move it out of render .

We can keep the input focused by writing:

import React from 'react';
import styled from 'styled-components';

const Container = styled.div``;
const Input = styled.input``;

class App extends React.Component {
  constructor(){
    this.state = {
      name: ''
    };
    this.updateName = this.updateName.bind(this);
  }

  updateName(e){
    e.preventDefault();
    this.setState({ name: e.target.value });
  }

  render() {
    return (
      <Container>
        <Input
          type="text"
          onChange={this.updateName}
          value={this.state.name}
        />
      </Container>
    )
  }
}

We move the Input and Container outside the App component to create the components outside rather than on the fly inside.

This means that the same instance will always be used.

And the input will stay focused when App rerenders.

Conclusion

We shouldn’t create any components on the fly without our component so that the same instance is always used.

We can create our own component to scroll a component to the top when we navigate to a different route.

withRouter injects a few objects to our component.

Categories
React Tips

React Tips — Infinite Scroll, Submit, Focus, and Drag and Drop

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.

onSubmit in React

To run a submit handler in a React app, we should call preventDefault to prevent the default submit behavior, which is to submit to a server.

For instance, we write:

class App extends React.Component {

  submit(e){
    e.preventDefault();
    alert('submitted');
  }

  render() {
    return (
      <form onSubmit={this.submit}>
        <button type='submit'>click me</button>
      </form>
    );
  }
});

We called e.preventDefault() with the submit method, which we pass as the value of the onSubmit prop.

React onClick Being Called on Render

We’ve to pass in the reference to a function instead of calling it.

For instance, we write:

class Create extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    const playlist = this.renderPlaylists(this.props.playlists);
    return (
      <div>
        {playlist}
      </div>
    )
  }

  renderPlaylists(playlists) {
    const activatePlaylist = this.activatePlaylist.bind(this, playlist.id);
    return playlists.map(playlist => {
      return (
        <div key={playlist.id} onClick{activatePlaylist}>
          {playlist.name}
        </div>
      );
    })
}

We have:

this.activatePlaylist.bind(this, playlist.id)

which returns a function that changes the value of this to the current component.

Also, it passes the playlist.id as the argument to the this.activatePlaylist method.

Making React Component or Element Draggable

To create a draggable component easily, listen to the mousemove, mousedown, and mouseup events

For instance, we can write:

import React, { useRef, useState, useEffect } from 'react'

const styles = {
  width: "200px",
  height: "200px",
  background: "green",
  display: "flex",
  justifyContent: "center",
  alignItems: "center"
}

const DraggableComponent = () => {
  const [pressed, setPressed] = useState(false)
  const [position, setPosition] = useState({x: 0, y: 0})
  const ref = useRef()

  useEffect(() => {
    if (ref.current) {
      ref.current.style.transform = `translate(${position.x}px, ${position.y}px)`
    }
  }, [position])

  const onMouseMove = (event) => {
    if (pressed) {
      setPosition({
        x: position.x + event.movementX,
        y: position.y + event.movementY
      })
    }
  }

  return (
    <div
      ref={ref}
      style={styles}
      onMouseMove={ onMouseMove }
      onMouseDown={() => setPressed(true)}
      onMouseUp={() => setPressed(false)}>
      <p>drag me</p>
    </div>
  )
}

We have the Draggable component with some props.

We listen to the mousedown and mouseup events to set the pressed state to be false and true respectively.

This will let us dragged if the pressed state is true , which is when we’re dragging.

Then we add a listener for the mousemove event by passing the onMouseMove function to the onMouseMove prop.

Then we set the position in the onMouseMove function.

We set the position by changing the x and y coordinates of the div if pressed is true .

Infinite Scrolling with React

To add infinite scrolling easily with React, we can use the react-infinite-scroller package.

To install it, we run:

npm install react-infinite-scroller

Then we can use it by writing:

import React, { Component } from 'react';
import InfiniteScroll from 'react-infinite-scroller';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      listData: [],
      hasMoreItems: true,
      nextHref: null
    };
    this.fetchData = this.fetchData.bind(this);
  }

  async fetchData(){
    const listData  = await getJobsData();
    this.setState({ listData });
  }

  componentDidMount() {
     this.fetchData();
  }

  render() {
    const loader = <div className="loader">Loading ...</div>;
    const JobItems = this.state.listData.map(job => {
      return (<div>{job.name}</div>);
    });
    return (
      <div className="Jobs">
         <h2>Jobs List</h2>
         <InfiniteScroll
           pageStart={0}
           loadMore={this.fetchData.bind(this)}
           hasMore={this.state.hasMoreItems}
           loader={loader}
         >
            {JobItems}
         </InfiniteScroll>
      </div>
    );
  }
}

We use the InfiniteScroll component to add infinite scrolling to our app.

pageStart is the starting page number.

loadMore is the function to load more data.

hasMore is the state to see if we have more data.

loader is the loader component.

We get new data every time we load and scroll to the bottom of the page.

Select All Text in Input with React When it’s Focused

We can call the select method on the input to focus it.

For instance, we can write:

const Input = (props) => {
  const handleFocus = (event) => event.target.select();

  return <input type="text" value="something" onFocus={handleFocus} />
}

We have the handleFocus function that calls the select method on the input element to select the input value when it’s focused.

With a class component, we can write:

class Input extends React.Component {
  constructor(){
    super();
    this.handleFocus = this.handleFocus.bind(this);
  }

  handleFocus(event){
    event.target.select();
  }

  render() {
    return (
      <input type="text" value="something" onFocus={this.handleFocus} />
        );
    }
}

We have the handleFocus method to call select to select the input value when we focus the input.

Conclusion

We can use a package to add infinite scrolling easily.

Also, we can select the values of the input.

We can add a draggable item to our component without a library.

We’ve to call preventDefault to stop the default submit behavior.

Categories
React Tips

React Tips — Conditional Prop Validation, and React Router Basics

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.

Set isRequired on a Prop if Another Prop is null or Empty with Prop-Types

Prop-types lets us validate another prop with custom validation.

For instance, we can write:

import PropTypes from 'prop-types';

//...

Item.propTypes = {
  showDelete: PropTypes.bool,
  handleDelete: (props, propName, componentName) => {
    if (props.showDelete === true && typeof props[propName] !== 'function') {
      return new Error('handleDelete must be a function');
    }
  },
}

We have the Item component where we set the propTypes property to do some validation.

We have showDelete set to a boolean prop.

And handleDelete is validated by a function that checks if the showDelete prop is true and that the handleDelete prop is a function.

propName is the name of the prop that we’re currently validating.

So propName is the handleDelete prop.

We make sure that if props.showDelete is true , the handleDelete prop is a function.

There’s also the react-required-if package that lets us simplify our code.

For instance, we can write:

import requiredIf from 'react-required-if';
import PropTypes from 'prop-types';

//...

Item.propTypes = {
  showDelete: PropTypes.bool,
  handleDelete: requiredIf(PropTypes.func, props => props.showDelete === true)
};

We make sure that handleDelete is a function when showDelete is true with the requiredIf function.

Wrong Components in a List Rendered by React

If we’re rendering a list of items, we should assign a unique value for the key prop to each item in the list.

For instance, we can write:

<div>
  <div className="packages">
    <div key={0}>
      <button>X</button>
      <Package name="a" />
    </div>
    <div key={1}>
      <button>X</button>
      <Package name="b" />
    </div>
    <div key={2}>
      <button>X</button>
      <Package name="c" />
    </div>
  </div>
</div>

We have a list of items, and we assigned a unique value for each key prop.

This way, they’ll be rendered properly no matter what we do with them.

If we’re rendering an item, we should write:

consr packages = this.state.packages.map((package, i) => {
  return (
    <div key={package}>
      <button onClick={this.removePackage.bind(this, package)}>X</button>
      <Package name={package.name} />
    </div>
  );
});

We make sure that we pass in a unique value for the key prop.

Then when we manipulate the array and re-render it, everything will be rendered properly.

We should never use the array index as the key since they can change and may not be unique.

How to Create an App with Multiple Pages using React

To let us create an app that has multiple pages in React, we’ve to use React Router to do the routing.

We install it by running:

npm install react-router-dom

Then we write:

import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

const Home = () => {
  return <h2>Home</h2>;
}

const About = () => {
  return <h2>About</h2>;
}

const Profile = () => {
  return <h2>Profile</h2>;
}

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
            <li>
              <Link to="/profile">Profile</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/profile">
            <Profile />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

We created 3 function components which we use as the route’s content.

Then in the App component, we import the Switch component and add the routes inside.

The Route component lets us create the routes.

path is the URL path that we want to map to the component.

And we add the component we want to display inside the Route component.

Link is provided by React Router so that we go to the URL that we want to go to.

It can also work with nested routes, custom links, default routes, query strings, and URL parameters.

We can add nestyed routes by writing:

loads the Topics component, which renders any further <Route>'s conditionally on the paths :id value.import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useRouteMatch,
  useParams
} from "react-router-dom";

export default function App() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/topics">Topics</Link>
          </li>
        </ul>

        <Switch>
          <Route path="/topics">
            <Topics />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Topics() {
  let match = useRouteMatch();

  return (
    <div>
      <h2>Topics</h2>

      <ul>
        <li>
          <Link to='about'>About</Link>
        </li>
      </ul>

     <Switch>
        <Route path='/about' >
          <About />
        </Route>
      </Switch>
    </div>
  );
}

function About() {
  return <h2>About</h2>;
}

We just nest routes in components to nest them.

Conclusion

We create an app that has multiple pages with React Router.

Also, we can conditionally validate props with a function or a 3rd party library.

And we should always add a key prop with a unique value to each list item.