Categories
React Tips

React Tips — create-react-app, Disable Buttons, and Current 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.

How to get Current Route with React Router

We can use the useLocation hook to get the current route with React Router.

For instance, we can write:

import { useLocation } from 'react-router-dom'

function App() {
  const location = useLocation();
  return <span>{location.pathname}</span>
}

useLocation returns the nativelocation object.

So we can use location.pathname to get the current route.

With class components, we can use the withRouter higher-order component to set the location prop to do the same thing.

For instance, we can write:

import {withRouter} from 'react-router-dom';

const App = withRouter(props => <Foo {...props}/>);

class Foo extends React.Component {
  bar() {
    const {pathname} = this.props.location;
  }
}

We called the withRouter higher-order component and pass in the props from there into our Foo component.

This sets the locations prop of Foo to the native location object.

Then we can get the pathname for the path.

Services in React Application

A service is just a context-independent piece of code which we can reuse anywhere.

So to make a service in a React app, we can just make a module that exports an object with the shared methods that we want to call.

For instance, we can write:

const HttpService = {
  getTodos(value) {
     // code to get todos
  },

  setTodos(value) {
     // code to save todos
  }
};

export default HttpService;

Then in our component, we can write:

import HttpService from "./services/HttpService.js";

function TodosPage() {
  const [todos, setTodos] = useState([]);
  const getTodos = async () => {
    const todos = await HttpService.getTodos();
    setTodos(todos);
  }

  useEffect(() => {
    getTodos()
  }, [])

  return <Todos todos={todos} />
}

We just import the module and then use the getTodos method within the export object to get the data we want.

We can do the same thing for any other piece of context-independent shared code.

Disable a Button When an Input is Empty

We can disable a button when an input field is empty by checking the input’s value and determine if we need to disable button.

For instance, we can write:

class EmailForm extends React.Component {
  constructor() {
    super();
    this.state = {
      email: '',
    };
  }

  handleEmailChange = (evt) => {
    this.setState({ email: evt.target.value });
  }

  handleSubmit = () => {
    const { email } = this.state;
    alert(`${email} subscribed`);
  }

  render() {
    const { email} = this.state;
    const enabled = email.length > 0;
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type="text"
          placeholder="Email"
          value={this.state.email}
          onChange={this.handleEmailChange}
        />

        <button disabled={!enabled}>Subscribe</button>
      </form>
    )
  }
}

We created an input for entering an email address.

When we enter something into the input, then the handleEmailChange method runs.

It sets the email state which we use in the value prop of the input and in the render method for the button enabled check.

We set the enabled flag by checking if the email’s length is bigger than 0.

This means that something is filled into the box.

Then we set the disabled prop of the button to be !enabled to disable it when it’s empty.

The Use of the ‘react-scripts start’ Command

react-scripts is a set of scripts that’s provided by create-react-app .

It kicks off our project without configuration, so we don’t have to set up a React project from scratch ourselves.

react-scripts start sets up the dev environment and runs the dev server and enables hot reloading.

It has React, JSX, ES6, and Flow syntax support.

ES features beyond ES6 like the object spread operator can also be used.

CSS is autoprefixed so that we don’t have to add the prefixes ourselves.

A create-react-app project also comes with a unit test runner.

It’ll also warn us about common developer mistakes.

And it’ll compile the app for us into a production-ready bundle.

It also supports the development of progressive web apps.

All the tolls will be updated when we update create-react-app.

Conclusion

We can create a shared module to reuse context independent code.

create-react-app does a lot for us, so we should use it to create React apps.

There are a few ways to get the current route with React Router.

We can disable a button by setting a boolean expression as the value of the disabled prop.

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
React Hooks

Top React Hooks — Sync and Async State

Hooks contains our logic code in our React app.

We can create our own hooks and use hooks provided by other people.

In this article, we’ll look at some useful React hooks.

Redhooks

The Redhooks is a React library for creating a state container in our React apps.

To install it, we can run:

npm install --save redhooks

Then we can use it by writing:

import React from "react";
import Provider, { createStore, combineReducers, useStore } from "redhooks";

const counter = (state = 0, { type, payload }) => {
  switch (type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
};

const rootReducer = combineReducers({ counter });
const store = createStore(rootReducer);

const Counter = () => {
  const { state, dispatch } = useStore();

  return (
    <div>
      <p>{state.counter}</p>
      <button onClick={() => dispatch({ type: "INCREMENT" })}>increment</button>
      <button
        onClick={() => setTimeout(() => dispatch({ type: "DECREMENT" }), 3000)}
      >
        decrement
      </button>
    </div>
  );
};

export default function App() {
  return (
    <Provider store={store}>
      <Counter />
      <Counter />
    </Provider>
  );
}

We created the counter reducer like any other Redux reducer.

Then we pass that into the combineReducer function to return the rootReducer .

And we pass that into the createStore function to create the store.

Next, we create a Counter component that uses the useStore hook to get the state and dispatch variables.

state has the state.

dispatch is a function for dispatching actions.

We get the property from the state to get the state we want.

In App , we wrap the Provider component around our app and we set the store prop to our store so that we can use it.

It also integrates with React Router and we can use Redux middleware like redux-thunk and redux-saga with it.

region-core

We can use the region-core library to manage our state in our React components.

To install it, we can run:

npm i region-core

Then we can use it by writing:

import React from "react";
import { createRegion } from "region-core";

const region = createRegion("foo");

const handleChange = e => region.set(e.target.value);

export default function App() {
  const value = region.useValue();
  return <input value={value} onChange={handleChange} />;
}

We call the createRegion to create our state with an initial value.

Then we can use the returned region object to set the value of the state with region.set .

And we can get the value of the state with region.useValue .

We can also use the loadBy function to load async data.

For instance, we can write:

import React from "react";
import { createRegion } from "region-core";

const region = createRegion();

const asyncFuncion = async ({ name }) => {
  const res = await fetch(`https://api.agify.io/?name=${name}`);
  const data = await res.json();
  return data;
};

const loadUser = region.loadBy(asyncFuncion);

loadUser({ name: "michael" });

export default function App() {
  const { loading, error, fetchTime, value } = region.useProps();

  return (
    <div>
      <div>{JSON.stringify(value)}</div>
      <p>{loading}</p>
      <p>{error}</p>
      <p>{fetchTime}</p>
    </div>
  );
}

We call loadBy with an async function to load data asynchronously with it.

Then we can use the useProps method to get the data.

loading has the loading state.

error has the error if they exist.

fetchTime has the timestamp when the data is fetched.

value has the data resolved by the async function.

Conclusion

Redhooks is a state management solution similar to Redux.

region-core is a state management library for internal state.

Categories
React Hooks

Top React Hooks — State History and Validation

Hooks contains our logic code in our React app.

We can create our own hooks and use hooks provided by other people.

In this article, we’ll look at some useful React hooks.

react-use

The react-use library is a big library with many handy hooks.

useStateValidator

We can use the useStateValidator hook to validate the state that’s changed in our app.

For instance, we can write:

import React from "react";
import { useStateValidator } from "react-use";

const NumberValidator = s => [s === "" || (s * 1) % 2 === 0];

export default function App() {
  const [state, setState] = React.useState(0);
  const [[isValid]] = useStateValidator(state, NumberValidator);

  return (
    <div>
      <input
        type="number"
        min="0"
        max="10"
        value={state}
        onChange={ev => setState(ev.target.value)}
      />
      <br />
      {isValid !== null && <span>{isValid ? "Valid" : "Invalid"}</span>}
    </div>
  );
}

to use the useStateValidator to validate the state.

We pass in the state as the first argument.

The 2nd is the NumberValidator to validate our state.

It takes the state and returns an array that has the validation state of the state .

It returns an array that’s nested in an array with the validation state.

Then we can use that anywhere we like.

useStateHistory

The useStateHistory hook lets us store a set amount of previous state values and provides handles to travel through them.

To use it, we can write:

import React from "react";
import { useStateWithHistory } from "react-use";

export default function App() {
  const [state, setState, stateHistory] = useStateWithHistory(2, 10, [1]);

  return (
    <div>
      <button onClick={() => setState(state + 1)}>increment</button>
      <p>{JSON.stringify(stateHistory, null, 2)}</p>
      <p>{JSON.stringify(state)}</p>
    </div>
  );
}

We use the useStateWithHistory hook with a few arguments.

The first argument is the initial state.

The 2nd argument is the maximum number of items to keep.

The 3rd is the initial history.

It returns an array with the state which has the state value.

setState lets us update the state.

stateHistory has the state history.

It’s an object with various methods.

The position has the position in history.

capacity has the max capacity of the history list.

back lets us move backward in the state history.

forward lets us move forward in the state history.

go lets us go to an arbitrary position in the state history.

useMultiStateValidator

The useMultiStateValidator hook lets us invoke a validator function if any state changes.

For instance, we can write:

import React from "react";
import { useMultiStateValidator } from "react-use";

const NumberValidator = s => [s.every(num => num % 2 === 0)];

export default function App() {
  const [state1, setState1] = React.useState(1);
  const [state2, setState2] = React.useState(1);
  const [state3, setState3] = React.useState(1);
  const [[isValid]] = useMultiStateValidator(
    [state1, state2, state3],
    NumberValidator
  );

  return (
    <div>
      <input
        type="number"
        min="1"
        max="10"
        value={state1}
        onChange={ev => {
          setState1(ev.target.value);
        }}
      />
      <input
        type="number"
        min="1"
        max="10"
        value={state2}
        onChange={ev => {
          setState2(ev.target.value);
        }}
      />
      <input
        type="number"
        min="1"
        max="10"
        value={state3}
        onChange={ev => {
          setState3(ev.target.value);
        }}
      />
      {isValid !== null && <span>{isValid ? "Valid" : "Invalid"}</span>}
    </div>
  );
}

We have 3 states, which we can set with their own input box.

And we call useMultiStateValidator with the numbers and the NumberValidator to validate the numbers.

NumberValidator takes has an array with the array we passed in as the argument.

And it returns an array with the validation condition of all the entries.

It returns a nested array with the validation state of all the numbers.

It’ll only return true if all of them meet the criteria in the callback for every as we specified.

Conclusion

The react-use library lets us validate states and list the past values of a state in an array.

Categories
React Hooks

Top React Hooks — State, Arrays, and Observables

Hooks contains our logic code in our React app.

We can create our own hooks and use hooks provided by other people.

In this article, we’ll look at some useful React hooks.

react-use

The react-use library is a big library with many handy hooks.

useObservable

The useObservable hook lets us get the latest value of an observable.

To use it, we can write:

import React from "react";
import { useObservable } from "react-use";
import { BehaviorSubject } from "rxjs";

const counter$ = new BehaviorSubject(0);

export default function App() {
  const value = useObservable(counter$, 0);

  return (
    <button onClick={() => counter$.next(value + 1)}>Clicked {value}</button>
  );
}

We installed Rxjs so that we can use the BehaviorSubject function to return an observable.

Then we pass that into the useObservable function to return the value from the observable.

We can use the next method from the counter$ observable to update its value.

useRafState

To update the state in the callback of requestAnimationFrame , we can use the useRafState hook.

For instance, we can use it by writing:

import React from "react";
import { useRafState, useMount } from "react-use";

export default function App() {
  const [state, setState] = useRafState({
    width: 0,
    height: 0
  });

  useMount(() => {
    const onResize = () => {
      setState({
        width: window.innerWidth,
        height: window.innerHeight
      });
    };

    window.addEventListener("resize", onResize);

    return () => {
      window.removeEventListener("resize", onResize);
    };
  });

  return <pre>{JSON.stringify(state, null, 2)}</pre>;
}

We add the useRafState hook with the object holding the initial value as the argument.

Then we use useMount to attach the window listeners for the resize event.

We update th width and height of it as the window resizes.

useSetState

The useSetState hook provides us with a way to set the state as we did with setState in class components.

For example, we can write:

import React from "react";
import { useSetState } from "react-use";

export default function App() {
  const [state, setState] = useSetState({});

  return (
    <div>
      <pre>{JSON.stringify(state, null, 2)}</pre>
      <button onClick={() => setState({ name: "james" })}>set name</button>
      <button onClick={() => setState({ foo: "bar" })}>foo</button>
      <button
        onClick={() => {
          setState(prevState => ({
            count: (prevState.count || 0) + 1
          }));
        }}
      >
        count
      </button>
    </div>
  );
}

We called the useSetState hook to return the state and setState variables.

state has the state.

And setState lets us set the state as we did with setState in class components.

It can take an object or a callback that has the previous state as the parameter and return the new state,

useStateList

The useStateList hook lets us iterate over state lists.

For instance, we can write:

import React from "react";
import { useStateList } from "react-use";

const stateSet = ["foo", "bar", "baz"];

export default function App() {
  const {
    state,
    prev,
    next,
    setStateAt,
    setState,
    currentIndex
  } = useStateList(stateSet);
  const indexInput = React.useRef(null);
  const stateInput = React.useRef(null);

  return (
    <div>
      <pre>
        {state} [index: {currentIndex}]
      </pre>
      <button onClick={() => prev()}>prev</button>
      <br />
      <button onClick={() => next()}>next</button>
      <br />
      <input type="text" ref={indexInput} />
      <button onClick={() => setStateAt(indexInput.current.value)}>
        set state by index
      </button>
      <br />
      <input type="text" ref={stateInput} />
      <button onClick={() => setState(stateInput.current.value)}>
        set state by value
      </button>
    </div>
  );
}

We called the useStateList hook with an array and it returns various properties.

state has the array state,

prev is a function to set the state and currentIndex to the previous entry’s value and index.

next is a function to set the state and currentIndex return the next entry’s value and index.

setStateAt lets us get the value by index and set that as the state and currentIndex ‘s value.

And setState takes the entry’s content and returns the state and currentIndex ‘s value for that.

Conclusion

The react-use library has hooks to let us use observables, set state, and traverse arrays.