Categories
React Tips

React Tips — Scroll to Element, Load Fonts, Run setState in Sequence

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

Access Component Methods from the Outside in React

We can access component methods from the outside if we assign a ref to the component.

For instance, we class components, we can write:

const ref = React.createRef()

const parent = (<div>
  <Child ref={ref} />
  <button onClick={e => console.log(ref.current)}>click me</button>
</div>)

We create the ref with the React.createRef method.

Then we pass the ref to the Child component.

Then we can get the component with the ref.current property as we did in the onClick callback.

Scroll to an Element in a React Component

We can scroll to an element within a React component by using the window.scrollTo method with a ref.

For instance, in function components, we can write:

import React, { useRef } from 'react'

const scrollToRef = (ref) => window.scrollTo(0, ref.current.offsetTop);

const App = () => {
  const myRef = useRef(null)
  const scrollTo = () => scrollToRef(myRef)
  return (
    <>
      <div ref={myRef}>scroll to me</div>
      <button onClick={scrollTo}>click me to scroll</button>
    </>
  )
}

We create the scrollToRef function to call scrollTo .

We want to scroll to the top of the element, so get ref.current.offsetTop to get that and use that as the 2nd argument.

Then in App , we called the useRef hook to create our ref.

Then we can pass our ref to the div to assign it.

Once we did that, we add the scrollTo method to scroll to the div which is assigned to our ref.

With class components we can do the same thing.

For instance, we can write:

class App extends Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }

  render() {
    return <div ref={this.myRef}></div>
  }

  scrollToRef = () => window.scrollTo(0, this.myRef.current.offsetTop)
}

We create our ref with React.createRef and then scroll to it with the scrollToRef method.

We get the element with the current property.

The scrolling logic is the same.

If we use a ref callback to assign our ref, then we can write:

class App extends Component {

  render() {
    return <div ref={(ref) => this.myRef = ref}></div>
  }

  scrollToMyRef = () => window.scrollTo(0, this.myRef.offsetTop)
}

We pass in a callback to the ref prop instead of using createRef to create our ref.

Then we get the element that’s assigned to the ref without the current property.

Run a Function After setState is Finished Updating

We can run a function after setState finishes updating by passing in a callback into the 2nd argument of setState .

For instance, we can write:

const promiseState = async state => new Promise(resolve => this.setState(state, resolve));

We created a promise with a async function that has resolve as the 2nd argument.

Then we can write:

promiseState({...})
  .then(() => promiseState({
    //...
  })
  .then(() => {
    //...
    return promiseState({
      //...
    });
  })
  .then(() => {
    //...
  });

Then we can call setState as many times as we want.

Add Fonts to create-react-app Based Projects

To add fonts to a create-react-app project, we can put it into the CSS code.

For instance, we can write:

@font-face {
  font-family: 'SomeFont';
  src: local('SomeFont'), url(./fonts/SomeFont.woff) format('woff');
}

in index.css .

Then we can import it by writing:

import './index.css';

We defined a new font with the font-face rule.

src has the location of the font.

font-family has the font name.

We can import CSS like a module with Webpack.

We can also put fonts in index.html .

For instance, we can write:

<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet">

or write:

<style>
    @import url('https://fonts.googleapis.com/css?family=Montserrat');
</style>

Another solution is to use the webfontloader package.

To install it, we run:

yarn add webfontloader

or

npm install webfontloader --save

Then we can write:

import WebFont from 'webfontloader';

WebFont.load({
   google: {
     families: ['Open Sans:300,400,700', 'sans-serif']
   }
});

to load the font we want.

Conclusion

There are various ways to load fonts into our create-react-app project.

We can access component methods from outside a component by assigning a ref to it and then we can call the methods.

This applies to class components.

There are various ways to scroll to an element.

We can run setState in sequence if we pass in a function into the 2nd argument of it.

Categories
React Tips

React Tips — React and Redux, Navigation, Drop Down Values

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

Setting State When a Drop Down Value is Changed

We can set the selected value from the dropdown as a state value by setting the key dynamically and the value to the event.target.value .

For instance, we can write:

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = { fruit: '' };
  }

  handleChange = (event) => {
    this.setState({ [event.target.name]: event.target.value });
  }

  render() {
    return (
      <select value={this,state.fruit} name='fruit' `handleChange={this.handleChange}`>
        <option value="apple">Apple</option>
        <option value="banana">Banana</option>
        <option value="cranberry">Cranberry</option>
      </select>
    )
  }

}

We set the fruit state to the selected value with the handleChange method.

It just calls setState to set the fruit state with the value, which is stored in event.target.value .

Update Nested State Properties in React

We can set nested state properties with setState .

It takes a callback with the previous state, then we return an object with the new state.

For instance, we can write:

this.setState(state=> ({ ...this.state.someProp, flag: false} }));

Or we can write:

this.setState({ someProperty: { ...this.state.someProperty, flag: false} });

to update the state without the callback.

In either example, we merge the new value to the old object with the spread operator.

Get First N Number of Elements from an Array

We can get the first N number of elements from an array with the slice method.

For instance, we can write:

const size = 5;
const items = list.slice(0, size).map(i => {
  return <Foo item={i} key={i.id} />
}

return (
  <div>
    {items}
  </div>
)

inside the render method or the function component.

We get the first 5 items with the slice method.

Then we use map to return the list of items to render.

And then we can put that anywhere.

Push to History in React Router

We can use the this.props.history.push method of React Router if we use it with a class component.

For instance, we can write:

import React from "react";
import { withRouter } from "react-router-dom";

class Foo extends React.Component {
  //...
  go() {
    this.props.history.push("/some/path");
  }
  //...
}
export default withRouter(Foo);

We have the go method inside our React component to call this.prop.history.push with the path we want to go to.

It’s available because we called the withRouter higher-order component to add the method.

With the hooks version of React Router, we can write:

import { useHistory } from "react-router-dom";

function App() {
  const history = useHistory();

  const goHome = () => {
    history.push("/home");
  }

  return (
    <button type="button" onClick={goHome}>
      Go home
    </button>
  );
}

We use the useHistory hook to get the history object and we call push on it to go to the path we want.

React Redux mapDispatchToProps

With React Redux, we use the mapDispatchToProps method to map dispatch actions to props.

For instance, we can write:

class App extends Component {
  sendAlert(){
    this.props.sendAlert()
  }

  render() {
    <div>
      <h1>alert: {this.props.alert}</h1>
      <Button onClick={sendAlert}/>
    </div>
  }
}

function mapDispatchToProps(dispatch) {
  return({
    sendAlert: () => {dispatch(ALERT_ACTION)}
  })
}

function mapStateToProps(state) {
  return { alert: state.alert }
}

export const FancyButtonContainer = connect(
  mapStateToProps, mapDispatchToProps)(
  App
)

We have the this.propss.sendAlert method, which is the one that’s returned with nmapDispatchToProps .

mapDispatchToProps is a function that we can use with the connect higher-order component to map the dispatch action to props.

Likewise, mapStateToProps lets us return the state from the Redux store and make it available as a prop in our component.

So this.props.alert is the value of the alert property in the returned object.

Update the Parent’s State

We can update the parent;’s state by passing in a function from the parent to the child as a prop.

Then in the child component, we can call it to update the state.

For instance, we can write:

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

  handler() {
    this.setState({
      foo: 'bar'
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <button onClick={this.props.handler}>click me</button>
  }
}

We pass the this.handler method to the Child component.

We’ve to remember to call bind with this on it so the value of this will be the parent.

Then we can call it in the Child by passing it into the onClick prop as this.props.handler .

Conclusion

We can pass functions from the parent component to the child.

We can set the state when the dropdown is changed.

There are a few ways to navigate with React Router.

Redux can be used to map actions and states to props of a component.

Categories
React Tips

React Tips — SVGs and Scroll Events

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.

Embedding SVG into ReactJS

We can put SVG code straight into our React component

For instance, we can write:

function SvgApple() {
  return (
       <svg id="svg2" viewBox="0 0 624.38 604.71" version="1.0">
      <defs id="defs3">
        <linearGradient
          id="linearGradient2847"
          x1="340.66"
          gradientUnits="userSpaceOnUse"
          y1="322.86"
          gradientTransform="translate(164.44 185.74)"
          x2="398.88"
          y2="322.86"
        >
          <stop id="stop2831" stop-color="#ccc" offset="0" />
          <stop id="stop2833" stop-color="#ccc" stop-opacity="0" offset="1" />
        </linearGradient>
        <radialGradient
          id="radialGradient2849"
          gradientUnits="userSpaceOnUse"
          cy="586.46"
          cx="366.79"
          gradientTransform="matrix(1 0 0 .90909 246.44 239.05)"
          r="258.49"
        >
          <stop id="stop2807" stop-color="#e6e6e6" offset="0" />
          <stop
            id="stop2809"
            stop-color="#e6e6e6"
            stop-opacity="0"
            offset="1"
          />
        </radialGradient>
        <radialGradient
          id="radialGradient2851"
          gradientUnits="userSpaceOnUse"
          cy="274.22"
          cx="490.32"
          gradientTransform="matrix(1 0 0 .61041 168.44 290.57)"
          r="123.39"
        >
          <stop id="stop2821" stop-color="#ccc" offset="0" />
          <stop id="stop2823" stop-color="#ccc" stop-opacity="0" offset="1" />
        </radialGradient>
      </defs>
      <g id="layer1" transform="translate(-89.704 -231.29)">
        <g
          id="g2837"
          transform="matrix(.93776 .34729 -.34729 .93776 206.95 -108.99)"
        >
          <path
            id="path10856"
            stroke="#000"
            transform="translate(731.28 508.81)"
            d="m-320.82 328.99c1.62-1.61-22.63-13.81-20.59-14.85 2.03-1.04-2.35 25.75-0.09 25.39 2.25-0.36-10.2-24.47-7.94-24.12 2.26 0.36-17.03 19.45-15 20.49 2.04 1.04 6.14-25.79 7.76-24.18 1.62 1.62-25.21 5.73-24.17 7.76 1.03 2.04 20.12-17.25 20.48-15 0.36 2.26-23.76-10.19-24.12-7.93-0.35 2.26 26.43-2.13 25.39-0.09-1.03 2.03-13.23-22.21-14.85-20.6-1.61 1.62 22.63 13.81 20.6 14.85-2.04 1.04 2.35-25.75 0.09-25.39s10.19 24.48 7.94 24.12c-2.26-0.36 17.03-19.45 14.99-20.49-2.03-1.03-6.14 25.79-7.75 24.18-1.62-1.62 25.21-5.72 24.17-7.76s-20.13 17.26-20.48 15c-0.36-2.26 23.76 10.19 24.11 7.93 0.36-2.25-26.42 2.13-25.39 0.1 1.04-2.04 13.24 22.21 14.85 20.59z"
          />
          <path
            id="path10853"
            stroke="#000"
            fill="red"
            d="m625.29 586.46c0 138.74-115.81 235-258.5 235-142.68 0-258.49-96.26-258.49-235s115.81-234.99 258.49-234.99c142.69 0 258.5 96.25 258.5 234.99z"
          />
          <path
            id="rect10858"
            stroke="#000"
            stroke-width=".62806"
            fill="#803300"
            d="m381.01 236.85c8.59 0 17.56 0.03 17.56 0.07l-14.31 171.88c0 0.04-6.92 0.07-15.51 0.07s-15.51-0.03-15.51-0.07l-12.26-171.88c0-0.04 31.44-0.07 40.03-0.07z"
          />
          <path
            id="path2827"
            opacity=".6729"
            fill="url(#linearGradient2847)"
            d="m381.01 236.85c8.59 0 17.56 0.03 17.56 0.07l-14.31 171.88c0 0.04-6.92 0.07-15.51 0.07s-15.51-0.03-15.51-0.07l-12.26-171.88c0-0.04 31.44-0.07 40.03-0.07z"
          />
          <path
            id="path10861"
            stroke="#000"
            stroke-width="3.063"
            fill="none"
            d="m337 394.28c2.62 5 12.11 14.08 19.7 14.09 7.77 0.02 27.78 0.58 29.61-1.57 3-3.54 10.81-15.89 12.33-20.76"
          />
          <path
            id="path10868"
            stroke="#000"
            stroke-width=".35974"
            fill="#338000"
            d="m538.62 313.49c-56.1 31.56-164.82 43.23-171.29 31.7-6.46-11.54 64.93-89.87 121.02-121.43 56.1-31.56 117.89-28.15 124.35-16.61 6.46 11.53-17.99 74.78-74.08 106.34z"
          />
          <path
            id="path2803"
            opacity=".40654"
            fill="url(#radialGradient2849)"
            d="m707.29 586.46c0 138.74-115.81 235-258.5 235-142.68 0-258.49-96.26-258.49-235s115.81-234.99 258.49-234.99c142.69 0 258.5 96.25 258.5 234.99z"
          />
          <path
            id="path2817"
            opacity=".78364"
            fill="url(#radialGradient2851)"
            d="m542.62 311.49c-56.1 31.56-164.82 43.23-171.29 31.7-6.46-11.54 64.93-89.87 121.02-121.43 56.1-31.56 117.89-28.15 124.35-16.61 6.46 11.53-17.99 74.78-74.08 106.34z"
          />
        </g>
      </g>
    </svg>
  );
}

We added an SVG of an apple from https://publicdomainvectors.org/en/free-clipart/Apple-vector-drawing/2168.html by removing all the namespaced tags from the original SVG code.

Update Style of a Component onScroll in React

We can listen to scroll events by attaching a scroll event listener in our component.

For instance, we can write:

class App extends React.Component {
  constructor(){
    this.state = {}
  }

  componentDidMount() {
    window.addEventListener('scroll', this.handleScroll);
  },

  componentWillUnmount() {
    window.removeEventListener('scroll', this.handleScroll);
  },

  handleScroll() {
    const scrollY = window.scrollY;

    this.setState({
      scrollY
    });
  }

  render(){
    return <div>{this.state.scrollY}</div>;
  }
}

We attached the scroll event listener with the addEventListener method in the componentDidMount method to add the event listener when the component mounts.

Then we call the removeEventListener to remove the listener.

handleScroll is our listener.

We get the scroll distance with the window.scrollY property.

And we call setState to set the scrollY state so that we can use it anywhere in our component.

In the componentWillUnmount method, we remove the scroll event listener when the component unmounts.

If we have a function component, we write:

import React, { useState, useEffect } from "react"

const ScrollingElement = () => {
  const [scrollY, setScrollY] = useState(0);

  function handleScroll() {
    setScrollY(window.scrollY);
  }

  useEffect(() => {
    function watchScroll() {
      window.addEventListener("scroll", handleScroll);
    }
    watchScroll();

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return (
    <div>
      <div>{scrollY}px</div>
    </div>
  );
}

The code is equivalent to class example.

We add the listen in the useEffect callback to listen to the scroll event.

Then we stop listen by removing the listener within the callback that’s returned in the useEffect callback.

The handler calls the setScrollY function to set the scrollY state.

Conclusion

We can embed SVG directly in our React component as long as we remove all the namespaced tags.

To listen for scroll events, we attach a scroll event listener within our component.

Categories
React Tips

React Tips — Pass Props, Background Image, and Window Resize

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 to Handler Component with React Router

We can pass props to the route handler component with render props.

For instance, we can write:

<Route path="/greeting/:name" render={(props) => <Greeting greeting="Hello, " {...props} />} />

Then we can define our Greeting component by writing:

class Greeting extends React.Component {
  render() {
    const { text, match: {params} } = this.props;
    const { name } = params;

    return (
      <>
        <p>
          {text} {name}
        </p>
      </>
    );
  }
}

We pas in props from the parameter to the Greeting component.

Then we get the props from the this.props property in Greetings .

Then we can do whatever we want with it.

Rerender View on Browser Resize

We can watch for window resize with a hooked if we’re using function components.

For instance, we can write:

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

function useWindowSize() {
  const [size, setSize] = useState([0, 0]);
  useLayoutEffect(() => {
    function updateSize() {
      setSize([window.innerWidth, window.innerHeight]);
    }
    window.addEventListener('resize', updateSize);
    updateSize();
    return () => window.removeEventListener('resize', updateSize);
  }, []);
  return size;
}

function App() {
  const [width, height] = useWindowSize();
  return <span>{width} x {height}</span>;
}

We created the useWindowSize hook that watches the window size change by adding an event listener for the resize event.

Then when we return the function to clean up the code, we call removeEventListener to remove the event listener.

We set the size state in the event handler.

The window.innerWidth has the window width and the window.innerHeight has the window height.

Then we can get it the latest values by using our hook in a component.

We create our own hook to encapsulate our logic so that we don’t have to spread them in our code.

Also, we can reuse it anywhere, which is another benefit.

In class components, we can also watch the resize event.

For instance, we can write:

import React from 'react';

class App extends React.Component {
  state = { width: 0, height: 0 };
  render() {
    return <span>{this.state.width} x {this.state.height}</span>;
  }

  updateDimensions = () => {
    this.setState({ width: window.innerWidth, height: window.innerHeight });
  };

  componentDidMount() {
    window.addEventListener('resize', this.updateDimensions);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.updateDimensions);
  }
}

We have the updateDiemsions method to set the window width and height with setState .

Then we call addEventListener in componentDidMount to listen to window size changes.

And we call removeEventListener to remove the listener when the component unmounts.

In the render method, we render the window size.

Fix ‘Uncaught TypeError: Cannot read property ‘setState’ of undefined’ Error

We can fix the setState is undefined error by calling bind on the method that we’re calling in our class component.

For instance, we write:

constructor(props) {
  super(props);
  this.state = {
    count: 1
  };

  this.setCount = this.setCount.bind(this);
}

We call this.setCount.bind(this) to set the value of this of this.setCount to the component.

We call it in the constructor so that we don’t have to call bind multiple times anywhere else.

Also, we can use an arrow function instead.

For instance, we can write:

setCount ` = () => {
  this.setState({
    count : this.state.count + 1
  });
}`

in our component class.

React Router with an Optional Path Parameter

We can add an optional path parameter by adding a question mark to the path parameter.

For instance, we can write:

<Route path="/to/page/:pathParam?" component={Page} />

for one optional parameter.

If we want more, we can write:

<Route path="/to/page/:foo?/:bar?" component={Page} />

This is applicable to version 4 or later.

Setting a Background Image With React Inline Styles

We can set the background image with React inline styles by writing:

backgroundImage: `url(${Background})`

inside the object that we pass into the styles prop.

Backgeround is the image that we imported as a module.

An image can be imported as a module with Webpack.

Conclusion

We can set the background image by import the image as a module and setting that as the value of the background image.

To get the size of the window, we can listen to the resize event.

We can pass props to a route handling component with React Router.

Categories
React Tips

React Tips — Fix Common Errors, Multiple Classes, and Context API

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.

Add Multiple Classes to a React Component

We can use the classnames package to add multiple classes to a React component.

For instance, in the render method or the function component, we can write:

const liClasses = classNames({
  'main-class': true,
  'active': props.active
});

return (<li className={liClasses}>{props.name}</li>);

We can also use template literals to do the same thing.

For instance, we can write:

<input className={`form-control rounded ${this.state.valid ? '' : 'error'}`} />

Fix the ‘Invariant Violation: Objects are not valid as a React child’ Error

We can fix the error by making sure that we have strings or components in between the opening and closing tags of a wrapper component.

For instance, if we’re rendering a string, we can write:

return (
  <Item href={routeString}>
    {breadcrumbElement}
  </Item>
)

where breadcrumbElement is a React element or component.

We can also replace the element or component with a string:

return (
  <Item href={routeString}>
    hello world
  </Item>
)

If we have an array, we can write:

const photosList = photos.map((photo, i) => {
  return (
    <div>
       <img src={photo.url} alt={photos.description} />
    </div>
  );
});

return (
  {photosList}
);

These are all inside the render method of a class component or a function.

We have an expression to render the photos array into images by call map with a callback that returns an img element.

Then we return that in our return statement.

Fix the ‘A component is changing an uncontrolled input of type text to be controlled’ Error

We can fix the error by setting the value prop of our input with a state.

To set the state, we pass an event handler to the onChange prop to set the state when the input value changes.

For instance, we can write:

<input
  className="input"
  type="text"
  value={this.state.name || ""}
  name="name"
  placeholder="name"
  onChange={this.onChange}
/>

Then in our onChange method, we can write:

onChange(event){
  const { name, value } = event.target;
  this.setState(prevState => {
    prevState.fields[name] =  value;
    return {
      fields: prevState.fields
    };
  });
};

We get the name and value properties from event.target .

Then we call setState with a callback to merge the new value with the existing state object.

Two React Components Communicating

There are a few scenarios where React components communicate.

The most common is parent-child communication.

For instance, we can write:

const Child = ({ onClick }) => (
  <div onClick={() => onClick(42)}>
    Click me
  </div>
);

class Parent extends React.Component {
  onClick(value){
    console.log(value);
  };

  render() {
    return (
      <Child onClick={this.onClick}/>
    )
  }
}

We have the onClick method in Parent that we pass to the Child component.

Then we get the onClick from the props from the props parameter.

Then we pass that to the onClick callback where we call it with a value.

Then the console.log will run.

If we want to communicate between components that have other relationships, we can use the Context API.

For instance, we can write:

const AppContext = React.createContext(null)

class App extends React.Component {
  render() {
    return (
      <AppContext.Provider value={{ language: "en" }}>
        <div>
          <Foo>
            <Bar>
              <Baz />
            </Bar>
          </Foo>
        </div>
      </AppContext.Provider>
    )
  }
};

const Baz = () => (
  <AppContext.Consumer>
    {({language}) => <div>{language}</div>}
  </AppContext.Consumer>
);

We call the React.createContext to created the context.

Then we can use the AppContext.Provider to pass the data into any components that are inside the AppContext.Provider .

The value prop has the data that we can access elsewhere.

Since our Baz component is inside our context provider, we can use the AppContext.Consumer to get the data and render it.

We have a callback inside it to get the language from the object we passed into value .

Conclusion

We can add multiple classes to a component with a template string or the classnames package.

We can communicate between parent and child components directly.

Or we can use the context API to communicate between any component.

Various errors can be solved with quick fixes.