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.

Categories
React Tips

React Tips — JWT and Redux, Run Code After setState and set Body Styles

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.

setState is One Step Behind onChange

To make sure that we set our state sequentially within a React component, we can pass in a callback as a 2nd argument of setState .

The callback is run when the value in the first argument is set as the new state value.

For instance, we can write:

handleChange(e) {
  this.setState({ name: e.target.value }, this.handleSubmit);
}

We update the name state with our inputted value.

And then we call the handleSubmit method, which is a method that’s called after the name state is set.

In function components, we can write:

useEffect(() => {
  //...
}, [name]);

to run the callback when the name state changes.

How to Sync Props to State using React Hooks

To sync prop values to state in a function component, we can use the useEffect hook to watch for prop value changes.

Then the callback we pass into the hook is run and when the prop value changes.

For instance, we can write:

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

const Persons = ({ name }) =>  {
  const [nameState , setNameState] = useState(name);

  useEffect(() => {
    setNameState(name);
  }, [name])

  return (
    <div>
       <p>My name is {props.name}</p>
       <p>My name is {nameState}</p>
    </div>
  )
}

We have the Persons component that takes a name prop.

We set the initial state with the useState hook by passing in our prop value to it.

Then we added a useEffect hook to listen to the name value for changes.

If it changes, the callback is run.

We call setNameState to update the nameState with the latest name value.

Then we display both the prop and the state value.

Use Redux to Refresh a JSON Web Token

To use redux to refresh a JSON web token, we’ve to create a thunk and use the Redux thunk middleware.

To do that, we can write:

import { createStore, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import { reducer } from './reducer';

const rootReducer = combineReducers({
  reducer
  //...
})
const store = createStore(rootReducer, applyMiddleware(thunk));

to apply the thunk middleware in our store.

Then we can create our reducer by witing:

reducer.js

const initialState = {
  fetching: false,
};

export function reducer(state = initialState, action) {
  switch(action.type) {
    case 'LOAD_FETCHING':
      return {
        ...state,
        fetching: action.fetching,
      }
   }
}

We have a reducer that stores the fetching state.

Then we can create our think by writing:

export function loadData() {
  return (dispatch, getState) => {
    const { auth, isLoading } = getState();
    if (!isExpired(auth.token)) {
      dispatch({ type: 'LOAD_FETCHING', fetching: false })
      dispatch(loadProfile());
    } else {
      dispatch({ type: 'LOAD_FETCHING', fetching: true })
      dispatch(refreshToken());
    }
  };
}

We created a thunk that calls dispatch and also gets the state to check if we need to get the token with the refresh token.

A thunk returns a function with the dispatch function to dispatch actions and getState to get the state.

And then we can dispatch the thunk by writing:

componentDidMount() {
  dispath(loadData());
}

in our component.

There’s also the redux-api-middleware package that we can add as a middleware to our React app.

We can install it by running:

npm i redux-api-middleware

Then we can create an action with it by writing:

import { createAction } from `redux-api-middleware`;

const getToken = () => createAction({
  endpoint: 'http://www.example.com/api/token',
  method: 'GET',
  types: ['REQUEST', 'SUCCESS', 'FAILURE']
})

export { getToken };

type is a string that tells what action has occurred.

We also pass in the endpoint with the URL, and method with the request method.

We can add the middleware by writing:

import { createStore, applyMiddleware, combineReducers } from 'redux';
import { apiMiddleware } from 'redux-api-middleware';
import reducers from './reducers';

const reducer = combineReducers(reducers);
const createStoreWithMiddleware = applyMiddleware(apiMiddleware)(createStore);

export default function configureStore(initialState) {
  return createStoreWithMiddleware(reducer, initialState);
}

We import the reducers and the middleware and incorporate them into our store.

Then we can use the action that we created with createAction .

Conclusion

There are several ways to get a token with Redux.

Also, we can run code after setState is done.

And we can watch for prop value changes and update the state accordingly.

Categories
React Tips

React Tips — HTML, State Arrays, and Images

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 Pass HTML Tags in Props

There are several ways to pass HTML tags as props.

One way is to pass JSX expressions as props.

For instance, we can write:

myProp={<div><Foo />Some String</div>}

We can also pass in an HTML string:

myProp="<div>This is some html</div>"

Then we can render it as HTML by writing:

<div dangerouslySetInnerHTML={{ __html: this.props.myProp }}></div>

We set the dangerouslySetInnerHTML prop to render HTML as-is.

It only works with simple HTML and not JSX expressions, components, or other things.

We can also pass in an array of JSX elements:

myProp={["foo", <span>Some other</span>, "bar"]}

We have both strings and HTML in our myProp array.

We can then render this array the way we want.

Also, we can pass in components as the children of another component.

For instance, we can write:

<Foo>
  <div>Some content</div>
  <div>Some content</div>
</Foo>

We have the Foo component that’s wrapped around 2 divs.

In Foo we can render the components inside by referencing this.props.children for class components.

And in function components, we get the children property from the props parameter, which is the first one.

We can also use a fragment:

<MyComponent myProp={<React.Fragment>This is an <b>HTML</b> string.</React.Fragment>} />

Then we can pass in multiple elements without rendering a wrapper.

Implement Authenticated Routes in React Router

We can implement authenticated routes with our own components.

For instance, we can write:

const PrivateRoute = ({ component: Component, authed, ...rest }) => {
  return (
    <Route
      {...rest}
      render={(props) => authed
        ? <Component {...props} />
        : <Redirect to={{pathname: '/login', state: {from: props.location}}} />}
    />
  )
}

We created our own PrivateRouter component that takes the component that we want to protect.

We renamed the component prop to Component to make it uppercase.

Then we render the component if authentication credentials are valid.

Otherwise, we return the Redirect component to redirect to an unprotected page.

Then we can use it by writing:

<PrivateRoute authed={this.state.authed} path='/profile' component={Profile} />

We pass in the component that we want into PrivateRouter to protect it.

React.cloneElement vs this.props.children

We need to use React.cloneElement if we need to do anything other than rendering the children components.

This is because this.prop.children is only a descriptor of the children.

For instance, if we have the following:

render() {
  return(
    <Parent>
      <Child>First</Child>
      <Child>Second</Child>
      <Child>Third</Child>
    </Parent>
  )
}

Then to add a prop to it, we need to write:

render() {
  return (
    <div>
      {React.Children.map(this.props.children, child => {
        return React.cloneElement(child, {
          onClick: this.props.onClick })
      })}
    </div>
  )
}

We’ve to call React.cloneElement to make a clone of each child to add an onClick handler to each child component.

Push into State Array

We can puts into a state array by concatenating the new entries to it.

This way, we don’t mutate the original array.

We don’t want to change the original since it’ll be overwritten on the next render.

For instance, we can write:

const arr = this.state.myArray.concat('new');
this.setState({ myArray: arr })

We can also use the spread operator:

this.setState({ myArray: [...this.state.myArray, 'new'] })
this.setState({ myArray: [...this.state.myArray, ...[1,2,3] ] })

The first one adds a single entry as we have above.

The 2nd merged the 2nd array into the first one and return it.

If we need to set the new array value based on the current array’s value, we can call setState with a callback that returns a new array based on the previous one.

For instance, we can write:

this.setState(prevState => ({
  myArray: [...prevState.myArray, "new"]
}))

We return the state with a new array.

Load Local Images with React

We can load local images by importing the image as a module.

For instance, we can write:

import React from 'react';
import logo from './logo.png';

function Header() {
  return <img src={logo} alt="Logo" />;
}

We import the image as a module and we put it straight into the src prop.

We can also do the same with require :

<img src={require('./logo.png')} />

Conclusion

We can add images by importing them.

There are several ways to pass HTML as props.

React.cloneElement is required for adding props to children.

There are several ways to push new data to a state array.

Categories
React Tips

React Tips — Testing, Redirects, and Markdown

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.

Pass Props with Redirect Component with React Router

We can pass props with React Router’s Redirect component by passing in the state value.

For instance, we can write:

<Redirect
  to={{
    pathname: '/order',
    state: { id: '123' }
  }}
/>

We pass in the pathname for the URL path.

And the state has the state we want to pass to the component.

And then we can write:

this.props.location.state.id

to get the id in the component.

Distinguish Left and Right Click Events with React

We can distinguish between left and right-click by checking the type property of the event object.

For instance, if we have:

return <p onClick={this.handleClick} onContextMenu={this.handleClick}>click me</p>

Then we can write:

handleClick(e) {
  if (e.type === 'click') {
    console.log('Left click');
  } else if (e.type === 'contextmenu') {
    console.log('Right click');
  }
}

This is detecting a synthetic event.

We can also detect left and right click with nativeEvent :

handleClick(e) {
  if (e.nativeEvent.which === 1) {
    console.log('Left click');
  } else if (e.nativeEvent.which === 3) {
    console.log('Right click');
  }
}

The which property has the click type.

1 is left click and 3 is for right-click.

Using Jest to Spy on Method Call in componentDidMount

We can use the spyOn method to check which component method is called.

for instance, we can write:

const spy = jest.spyOn(Component.prototype, 'someMethod');
const wrapper = mount(<Component {...props} />);
wrapper.instance().someMethod();
expect(spy).toHaveBeenCalled();

Component is the component we’re testing.

someMethod is the method we’re spying.

We mount the component and get the instance with the instance method.

someMethod is the method we called.

Then spy should be called.

And then we clear our spies with:

afterEach(() => {
  spy.mockClear()
})

after each test.

How to Capture Only Parent’s Click Event and not Children

We should call preventDefault to capture only the parent component’s click event and not the children’s.

For instance, we can write:

onParentClick(event) {
  event.preventDefault();
  console.log(event.currentTarget);
}

How to Render Markdown from a React Component

We can render markdown with the react-markdown library.

For instance, we can write:

import React from 'react'
import Markdown from 'react-markdown'

const code = "# header";

React.render(
  <Markdown source={code} />,
  document.getElementById('root')
);

We get the Markdown component form the package.

And then we pass in the code to the source prop to render it.

Also, we can convert it to HTML and then render than with dangerousSetInnerHTML with the marked library.

For instance, we can write:

import React from 'react';
import marked from 'marked';

class Markdown extends React.Component {
  constructor(props) {
    super(props);

    marked.setOptions({
      gfm: true,
      tables: true,
      breaks: false,
      pedantic: false,
      sanitize: true,
      smartLists: true,
      smartypants: false
    });
  }
  render() {
    const { text } = this.props,
    html = marked(text || '');

    return (
      <div>
        <div dangerouslySetInnerHTML={{__html: html}} />
      </div>
    );
  }
}

Markdown.propTypes = {
  text: React.PropTypes.string.isRequired
};

Markdown.defaultProps = {
  text: ''
};

We set the options with the setOptions method.

gfm means we render Github flavored Markdown.

tables means we render tables.

breaks mean we render line breaks with br.

pedantic means we conform to the original Markdown spec and don’t fix their bugs or behavior.

sanitize we sanitize the HTML.

smartLists uses the smartlist behavior of the Markdown spec.

smartypants means using smart typographic punctuation for things like quotes and slashes.

Testing with Jest Using Date Objects Produce Different Snapshots in Different Timezones

We can set the current time to be a constant time in Jest.

For instance, we can write:

Date.now = jest.fn(() => new Date(Date.UTC(2020, 7, 9, 8)).valueOf())

Then we can set the timezone when we run our tests.

In Mac and Linux, we add:

"test": "TZ=America/Los_Angeles react-scripts test --env=jsdom",

And in Windows, we add:

"test": "set TZ=America/Los_Angeles && react-scripts test --env=jsdom",

to package.json ‘s scripts section.

Conclusion

We can pass states to the Redirect component.

Also, we can set the time zone for the and date for Jest tests.

We can also spy on function calls with them.

We can also capture left and right clicks and clicks from parent elements only.

There are various ways to render Markdown in our React component.

Categories
React Tips

React Tips — Scroll to Top When Navigating

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.

Return Multiple Lines JSX in Another Return Statement in React

We can return multiple lines of JSX code in when we map them from an array by returning an array in the map callback.

For instance, we can write:

render() {
  return (
    {[1, 2, 3].map((n) => {
      return [
        <h3>Item {n}</h3>
        <p>{n}</p>
      ]
    }}
  );
}

We can also return a fragment to wrap around the components:

render() {
  return (
    {[1, 2, 3].map((n, index) => {
      return (
        <React.Fragment key={index}>
          <h3>Item {n}</h3>
          <p>{n}</p>
        </React.Fragment>
      )
    }}
  );
}

Get History on react-router

We can get the history with React Royer by calling the createBrowserHistory method.

For instance,e we can write:

import { Router } from 'react-router-dom'
import { createBrowserHistory } from 'history'
import App from './App'

const history = createBrowserHistory({
  //...
});

ReactDOM.render((
  <Router history={history}>
    <App />
  </Router>
), holder)

We can also use the withRouter higher-order component to inject the history object into a component.

For instance, we can write:

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

class App extends React.Component {
  render () {
    this.props.history;
  }
}

withRouter(App);

We called withRouter with App to inject the history prop to it.

Then in our App component, we can get the history with this.props.history .

Include a Font Awesome Icon in React’s render()

To include Font Awesome icons in our React app, we can install the package by running:

npm install --save font-awesome

Then we can include the bundled CSS by writing:

import '../node_modules/font-awesome/css/font-awesome.min.css';

or:

import 'font-awesome/css/font-awesome.min.css';

And then in our component, we write:

render() {
    return <div><i className="fa fa-spinner fa-spin"></i></div>;
}

We set the class names for the icon as the value of the className prop.

There’s also the react-fontawesome package that lets us use icons by including the them bundled React components in our components.

To install it, we run:

npm install --save react-fontawesome

Then we import it by adding:

const FontAwesome = require('react-fontawesome');

Then we can use it by writing:

class App extends React.Component {
  render() {
    return (
      <FontAwesome
        name='rocket'
        size='2x'
        spin
        style={{ textShadow: '0 1px 0 rgba(0, 0, 0, 0.1)' }}
      />
    );
  }
});

We use the FontAwesome component to add the icon.

React Router Scroll to Top on Every Transition

We can create our own component to scroll to the top and wrap that around the component that we want to scroll to the top.

For instance, we can write:

class ScrollToTop extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.location !== prevProps.location) {
      window.scrollTo(0, 0)
    }
  }

  render() {
    return this.props.children
  }
}

export default withRouter(ScrollToTop)

We call the withRouter prop with the ScrollToTop component.

Then we have the location prop available in ScrollToTop after that.

In the component, we check the location prop to see what the path is in the componentDidUpdate method.

If they’re different, that means we transitioned to a new route.

So we call window.scrollTo(0, 0) to scroll to the top of the page.

In render , we render the children prop so that we display the content of it.

Then, to use it, we write:

import ScrollToTop from './ScrollToTop';

//...

const App = () => (
  <Router>
    <ScrollToTop>
      <Foo />
    </ScrollToTop>
  </Router>
)

We wrap it our around Foo component to scroll it to the top when it’s loaded.

Also, we can do the same thing with hooks.

For instance, we can write:

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

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

  return null;
}

export default withRouter(ScrollToTop);

We use withRouter again so that we get the history prop.

But we call history.listen instead of checking the value of history .

We call window.scrollTo(0, 0) whenever the callback of history.listen runs.

It returns a function to remove the listen.

So we put that into the function that’s returned in the useEffect callback.

We pass in an empty array as the 2nd argument of useEffect to only load the callback when the component mounts.

Then we use it by writing:

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

Conclusion

There are several ways to scroll to the top when we navigate to a different route.

We can return multiple components with map in an array.