Categories
React Tips

React Tips — Query Strings, Wrappers, and Clicks Outside

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.

Fix ‘Adjacent JSX elements must be wrapped in an enclosing tag’ Error

All components must have an outer element surrounding them.

For instance, we can write:

return (
  <div>
    <Child1 />
    <Child2 />
  </div>
)

We have a div surrounding all the child elements.

Also, we can use a fragment to surround our components if we don’t want to render a wrapper element.

For instance, we can write:

return (
  <>
    <Child1 />
    <Child2 />
  </>
)

or:

return (
  <React.Fragment>
    <Child1 />
    <Child2 />
  </React.Fragment>
)

Correct Way to Modify State Arrays in React

To modify a state array correctly in React, we should call setState ‘s state change function with a callback that returns the new array.

This way, we know the new value is derived from the most current value.

For instance, we can write:

this.setState(prevState => ({
  array: [...prevState.array, newThing]
}))

We add newThing to the end of the array.

If we use a function component, we can write:

`const [arr, setArr] = useState([]);
`//...
setArr(prevArr => [...prevArr, newThing]);

Detect Click Outside React Component

We can detect clicks outside a React component by listening to the documen ‘s click event.

This way, we can handle clicks of any element.

For instance, we can write:

import React, { Component } from 'react';


export default class App extends Component {
  constructor(props) {
    super(props);

    this.setWrapperRef = this.setWrapperRef.bind(this);
    this.handleClickOutside = this.handleClickOutside.bind(this);
  }

  componentDidMount() {
    document.addEventListener('mousedown', this.handleClickOutside);
  }

  componentWillUnmount() {
    document.removeEventListener('mousedown', this.handleClickOutside);
  }

  setWrapperRef(node) {
    this.wrapperRef = node;
  }

  handleClickOutside(event) {
    if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
      alert('clicked outside');
    }
  }

  render() {
    return <div ref={this.setWrapperRef}>hello</div>;
  }
}

We call the docuyment.addEventListener method to listen to the click event in the componentDidMount hook.

And we remove the listener with the component unmounts with removeListener in the componentWillUnmount hook.

Then we set the ref of the div so that we can check which element is clicked handleclickOutside and if it’s inside the component with contains .

Likewise, we can do the same with function components with hooks.

For instance, we can write:

import React, { useRef, useEffect } from "react";


function useClickOutside(ref) {
  useEffect(() => {
    function handleClickOutside(event) {
      if (ref.current && !ref.current.contains(event.target)) {
        console.log("clicked outside");
      }
    }

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [ref]);
}

`export default function App() {
  const wrapperRef = useRef(null);` useClickOutside`(wrapperRef);

  return <div ref={wrapperRef}>hello</div>;
}`

We created the useClickOutside hook to add the event listener when the hooked loads.

Then in the function, we return in the useEffect callback, we remove the click listener.

We watch the ref for changes, so we have [ref] as the 2nd argument of useEffect .

Then we call useRef to create the ref, assign it to the div, and call useClickOutside with it.

How to Get Parameter Value from a Query String

If we’re using React Router, we can get the parameter value from the query string with the URLSearchParams constructor and the location.search prop.

For instance, we can write:

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

this.prop.location.search has the query string.

Then we parse it into an object with URLSearchParams constructor.

And we call get with the key of the query parameter we want to get.

Also, we can use:

this.props.match.params.foo

to get the query parameter with key foo .

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

import { useLocation } from 'react-router';
import queryString from 'query-string';

const App = React.memo((props) => {
  const location = useLocation();
  console.log(queryString.parse(location.search));

  return <p>search</p>;
}

We use the useLocation hook from React Router to get the location object from the hook.

Then we can use the queryString package to parse the query string.

We can also replace the query-string package with the URLSearchParams constructor:

import { useLocation } from 'react-router';

const App = React.memo((props) => {
  const location = useLocation();
  console.log(new URLSearchParams(location.search));

  return <p>search</p>;
}

Conclusion

We should wrap our components with a root element or fragment.

The right way to modify arrays is to pass in a function to setState or state change function.

We can watch for clicks outside a component by adding event listeners.

Also, we can get the query string from the component with React Router.

Categories
React Tips

React Tips — Copy to Clipboard, Comparing Old and New Values with Hooks

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 Copy Text to Clipboard

We can copy text to the clipboard by using the navigator.ckipboard.writeText method.

For instance, we can write:

<button
  onClick={() =>  navigator.clipboard.writeText('copy this to clipboard')}
>
  copy to clipboard
</button>

We copy the text in the string in the argument to the clipboard.

Also, we can use the react-copy-to-clipboard package to make our lives easier.

For instance, we can write:

import React from 'react';
import ReactDOM from 'react-dom';
import {CopyToClipboard} from 'react-copy-to-clipboard';

class App extends React.Component {
  state = {
    value: '',
    copied: false,
  };

  onChange({target: {value}}) {
    this.setState({value, copied: false});
  },

  render() {
    return (
      <div>
        <input value={this.state.value} onChange={this.onChange} />

        <CopyToClipboard text={this.state.value}
          onCopy={() => this.setState({copied: true})}>
          <span>Copy to clipboard with span</span>
        </CopyToClipboard>

      </div>
    );
  }
}

The package comes with the CopyToClipboard component.

It takes the text prop with the text that we want to copy to the clipboard.

The onCopy prop is run when the text is copied.

Inside the component, we have the content that we can click to do the copying.

Once the element is clicked, the content in the text prop will be copied to the clipboard.

We can also use the execCommand method to copy the content of a DOM element that’s selected to the clipboard.

For instance, we can write:

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

export default function CopyExample() {

const [copySuccess, setCopySuccess] = useState('');
  const textAreaRef = useRef(null);

  function copyToClipboard(e) {
    textAreaRef.current.select();
    document.execCommand('copy');
  };

  return (
    <div>
      {
       document.queryCommandSupported('copy') &&
        <div>
          <button onClick={copyToClipboard}>Copy</button>
          {copySuccess}
        </div>
      }
      <form>
        <textarea
          ref={textAreaRef}
          value='text to copy'
        />
      </form>
    </div>
  );
}

We have a functional component with the copyToClipboard to select the text from our text area.

The selection is done by:

textAreaRef.current.select();

textAreaRef is the ref that we assigned to the text area.

Then we call the execCommand with the 'copy' argument to copy the text that’s selected to the clipboard.

In the JSX we return, we check if the copy command is supported with:

document.queryCommandSupported('copy')

and display a button to let us copy the data if it is.

We also have the text area that has the stuff to copy.

Identifying Different Inputs with One onChange Handler

We can use one event handler for multiple inputs.

To do that, we can create an event handler function that takes an argument to identify the input that we’ve changed.

For instance, we can write:

class App extends React.Component {
  constructor() {
    super();
    this.state = { input1: 0, input2: 0 };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(input, value) {
    this.setState({
      [input]: value
    })
  }

  render() {
    return (
      <div>
        <input type="text" onChange={e => this.handleChange('input1', e.target.value)} />
        <input type="text" onChange={e => this.handleChange('input2', e.target.value)} />
      </div>
    )
  }
}

We pass in a callback that calls the handleChange method with the key of the state that we want to change when the text is entered.

This way, we can change the input that we want.

setState in handleChange has a computed property name instead of a fixed property.

Compare Old Values and New Values with the useEffect Hook

We can use the useRef hook to get the previous value.

And we can get the latest values from the component itself.

For instance, we can write:

const usePrevious = (value) => {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

const App = (props) => {
  const { amount, balance } = props
  const prevAmount = usePrevious({ amount, balance });
  useEffect(() => {
    if (prevAmount.amount !== amount) {
      //...
    }

    if (prevAmount.balance !== balance) {
      //...
    }
  }, [amount, balance])

  //...
}

We created the usePrevious hook to get the previous values with useRef .

We set the old values to that we passed into the hook function by setting the values to the ref.current property.

The previous values are then returned from the hook.

In the App component, we get the latest values from the props.

And we get the old values from the usePrevious hook.

Then we can compare them in the useEffect callback.

The array that we passed in has the values that we want to watch for changes for.

Conclusion

We can set the previous values with the useRef hook.

There are various ways to copy text from a component to the clipboard.

We can identify different inputs in a component by creating our own event handler to identify them.

Categories
React Tips

React Tips — Wrappers, Render Props, and setState Errors

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.

Fix the ‘Warning: setState(…): Cannot update during an existing state transition’ Error

To fix this error, we shouldn’t call methods in the render method.

For instance, in the constructor, we put our state change methods:

this.handleButtonTrue = this.handleButtonChange.bind(this, true);
this.handleButtonFalse = this.handleButtonChange.bind(this, false);

Then in our render method. we write:

<Button onClick={this.handleButtonTrue}>true</Button>
<Button onClick={this.handleButtonFalse}>false/Button>

When to use React setState Callback

We should use the React setState ‘s callback when we need to run some code that always runs after a state change.

For instance, we can write:

changeTitle(event) {
  this.setState({ title: event.target.value }, () => {
    this.validateTitle();
  });

},
validateTitle() {
  if (this.state.title.length === 0) {
    this.setState({ error: 'no blank title' });
  }
},

We call setState to change the title state.

Then we run validateTitle to validate the latest value of the title state.

React Prop Validation for Date Objects

We can validate that date objects are passed in as props with the instanceof method.

We can write:

PropTypes.instanceOf(Date)

to do the validation.

Best Way to Access Redux Store Outside a React Component

We can access a Redux store outside a React component by exporting the value returned from createStore .

Then we can use that value anywhere we like.

For instance, in store.js , we write:

const store = createStore(myReducer);
export store;

Then in app.js , we write:

import { store } from './store'
store.dispatch(
  //...
)

If we want to use multiple stores, we can write a function that creates a store if it doesn’t exist and return a promise with the store.

Then we can use that to get the store.

Wrapping One Component into Another

To let us wrap one component in another, we create a wrapper component with that takes the children prop

For instance, we can write:

const Wrapper = ({children}) => (
  <div>
    <div>header</div>
    <div>{children}</div>
    <div>footer</div>
  </div>
);

const App = ({name}) => <div>Hello {name}</div>;

const App = ({name}) => (
  <Wrapper>
    <App name={name}/>
  </Wrapper>
);

We create a Wrapper component which takes the children prop.

Then we create a App component which we put inside our Wrapper component.

We can also use render props to pass components into another.

This means we pass the whole render function into another component.

For instance, we can write:

class Wrapper extends React.Component {
  state = {
    count: 0
  };

  increment = () => {
    const { count } = this.state;
    return this.setState({ count: count + 1 });
  };

  render() {
    const { count } = this.state;

  return (
      <div>
        {this.props.render({
          increment: this.increment,
          count: count
        })}
      </div>
    );
  }
}

class App extends React.Component {
  render() {
    return (
      <Wrapper
        render={({ increment, count }) => (
          <div>
            <div>
              <p>{count}</p>
              <button onClick={() => increment()}>Increment</button>
            </div>
          </div>
        )}
      />
    );
  }
}

We created a Wrapper which takes a render prop that takes a function that renders components.

We passed in a value for the render prop in App .

Then that’s called within Wrapper with the object that as the increment and count properties.

The increment method is passed in from App and as well as the count state.

Validate the PropTypes of a Nested Object in React

We can validate prop types of a nested object bu using the shape method.

For instance, we can write:

import PropTypes from 'prop-types';

propTypes: {
  data: PropTypes.shape({
    firstName: PropTypes.string.isRequired,
    lastName: PropTypes.string
  })
}

We call the PropTypes.shape method with an object that describes the structure of the data prop.

We have 2 strings and isRequired indicates that it’s required.

How to Add Comments in React?

We can add comments by putting them between curly braces.

For instance, we can write:

<div>
  {/* button click */}
  <Button whenClicked={this.handleClick}>click me </Button>
  <List />
</div>

Out comment is within the curly braces.

They’re the same as regular JavaScript comments.

Conclusion

We can add comments if we put them between the curly braces.

We shouldn’t call setState in our render method.

A Redux store can be accessed outside a React component.

We can nest child components by creating a wrapper component that takes the children prop.

Or we can accept a render prop in our component that renders components.

Categories
React Tips

React Tips — super, Call Child Methods, Debounce, and Inlint 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.

Difference Between “super()” and “super(props)” in React when Using ES6 Classes?

The only difference between the 2 is when we need to access props in the constructor.

If we need to get them in the constructor, then we’ve to get the props from the parameters and pass it into super .

For instance, we write:

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    console.log(this.props);
  }

  render(){
    //...
  }
}

If we don’t, then we can skip it:

class MyComponent extends React.Component {
  constructor(props) {
    super()
  }

  render(){
    //...
  }
}

Inline Style Best Practices

We can put inline styles by passing in an objecty with the styles or className prop.

For instance, we can write:

<li
 className={classnames({ 'list-item': true, 'is-complete': item.complete })} />

We used the classnames package to dynamically set the class names of the li element.

Also, we can write:

<li style={Object.assign({}, fooStyles, barStyles)}>

We just set the styles by passing in an object with all the style properties.

The keys have the attributes and the values are the values.

Does render Get Called Any Time “setState” is Called?

setState is called every time render is called.

This is why we can’t use setState in the render method.

Otherwise, we’ll get an infinite render loop.

If we don’t want it to be called on every setState call, we should have the shouldComponentUpdate hook.

We can compare the state and props and return a boolean expression with the condition to notify React when the component should render.

showComponentUpdate gas the following signature:

shouldComponentUpdate(nextProps, nextState)

Perform Debounce in React

We can denounce with React by using the awesome-debounce-promise package,

For instance, we can write:

const searchAPI = text => fetch(`/search?text=${encodeURIComponent(text)}`);

const searchAPIDebounced = AwesomeDebouncePromise(searchAPI, 500);

class SearchInputAndResults extends React.Component {
  state = {
    text: '',
    results: null,
  };

  search = async text => {
    this.setState({ text, results: null });
    const result = await searchAPIDebounced(text);
    this.setState({ result });
  };
  //...
}

We use the AwesomeDebouncePromise function to delay the searchAPI function by 500 ms.

Then in the sesrch method, we use the promise returned by the package and use setState to set the data.

We can also use it with function components.

For instance, we can write:

const searchAPI = text => fetch(`/search?text=${encodeURIComponent(text)}`);

const searchAPIDebounced = AwesomeDebouncePromise(searchAPI, 500);`

const App = () => {
  const [results, setResults] = useState([]);
  const [text, setText] = useState(['');`

  search = async text => {
    const result = await searchAPIDebounced(text);
    `setResults`(result);
  };

 return (
    <div>
      <input value={setText} onChange={e => setText(e.target.value)} />
      <div>
        {results && (
          <div>
            <ul>
              {results.map(r => (
                <li key={r.name}>{r.name}</li>
              ))}
            </ul>
          </div>
        )}
      </div>
    </div>
  );
};

We use the same function and the useState hook to set the state when it’s done.

This is all done in the search function.

Call Child Method from Parent

We can call a child method from a parent by forwarding the ref to the child.

Then we can get the child’s method from the ref.

For instance, we can write:

const Child = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    hello() {
      console.log("hello Child");
    }
  }));

  return <h1>Hi</h1>;
});

const Parent = () => {
  const childRef = useRef();

  return (
    <div>
      <Child ref={childRef} />
      <button onClick={() => childRef.current.hello()}>Click</button>
    </div>
  );
};

We call forwardRef on the child with a callback that returns our component.

The useImperrativeHandle hook gets the ref and returns an object w8itht eh methods we want to call.

Then in the parent, we pass the ref to the ref prop.

Then we call current.hello to call the method in the child.

If we use class components, then it’s simpler.

For instance, we can write:

const { Component } = React;

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

  onClick = () => {
    this.child.current.hello();
  };

  render() {
    return (
      <div>
        <Child ref={this.child} />
        <button onClick={this.onClick}>Click</button>
      </div>
    );
  }
}

class Child extends Component {
  hello() {
    conole.log('hello child');
  }

  render() {
    return <h1>Hello</h1>;
  }
}

We create a Child with the hello method which we call from the Parent .

Then we just assign a ref to it and call it with current.hello .

Conclusion

We can call child component function from the parent.

If we need to access props in the constructor, then we need to pass them into super .

We can denounce event handlers with 3rd party libraries.

Categories
React Tips

React Tips — Default Props and TypeScript, Copy Text to Clipboard

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.

Display Loading Screen While DOM is Rendering in Class Components

We can create a loading display in a React class component by putting our code in a few hooks.

For instance, we can write:

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      loading: true,
    }
  }

  componentDidMount() {
    this.timerHandle = setTimeout(() => this.setState({ loading: false }), 2000);
  }

  componentWillUnmount(){
    if (this.timerHandle) {
      clearTimeout(this.timerHandle);
    }
  }

  render(){
    return !loading && <div>hello world</div>;
  }
}

We have an App component, which has a timer created by calling setTimeout in the componentDidMount hook. It sets the loading state to false after 2 seconds. It’s initially set to true in the constructor. In our render method, we check the loading state and render something if it’s false .

Default Property Value in React Component Using TypeScript

We can set the default prop value in a React component by setting the defaultProps property in the component class.

For instance, we can write:

interface ItemProps {
  foo: string;
  bar: string;
}

export class Item extends React.Component<ItemProps, {}> {
  public static defaultProps = {
    foo: "default"
  };

  public render(): JSX.Element {
    return (
      <span>{this.props.foo.toUpperCase()}</span>
    );
  }
}

We created the ItemProps interfaces with a few properties. Then we pass that into the generic type parameter of React.Component to specify the prop names and types that are allowed. Inside the class, we set the static defaultProps property to set the default value of a prop.

With function components, we can write:

interface ItemProps {
  foo?: string;
  bar: number;
}

const ItemComponent: FunctionComponent<PageProps> = (props) => {
  return (
    <span>{props.foo}, {props.bar}</span>
  );
};

ItemComponent.defaultProps = {
  foo: "default"
};

We create an interface and then we pass it into the type parameter of the FunctionComponent generic type. Then we set the props’ default values with the defaultProps property.

useState Set Method Not Reflecting Change Immediately

useState ‘s state setting methods won’t reflect the changes immediately. They’ll be reflected in the next render cycle. To watch for the latest value of the state, we can use the useEffect hook.

For instance, we ecan write:

useEffect(() => { console.log(movies) }, [movies])

to watch for the latest value of the movies state.

React Component Names Must Begin with Capital Letters

We should create React components with names that begin with capital letters. This way, we can distinguish them between regular HTML elements and React components.

Get Derived State from Props

We can get derived state from props with memoize-one package. It’ll help us cache the values that are computed from props.

To install it, we run:

npm i memoize-one

Then we can use it by writing:

import memoize from "memoize-one";

const computeDerivedState = (props) => {
  //..
  return derivedState;
}

class App extends React.Component {
  constructor(){
    this.getDerivedData = memoize(computeDerivedState);
  }

  render() {
    const derivedData = this.getDerivedData(this.props.someValue);
    //...
  }
}

We have a computeDerivedState function which takes the props and compute something from them. Then we can use it in our render function to compute the value or get the memorized one if needed.

How to Copy Text to Clipboard

We can copy text to the clipboard by using the document.execCommand method.

For instance, we can write:

import React from 'react';

class Copy extends React.Component {

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

  copyToClipboard(e){
    this.textArea.select();
    document.execCommand('copy');
  };

  render() {
    return (
      <div>
        {
         document.queryCommandSupported('copy') &&
          <div>
            <button onClick={this.copyToClipboard}>Copy</button>
            {this.state.copySuccess}
          </div>
        }
        <form>
          <textarea
            ref={(textarea) => this.textArea = textarea}
            value='foo bar'
          />
        </form>
      </div>
    );
  }

}

We have a text area with some text we can copy. It’s assigned a ref so we can access its content later when we copy the text. We check if the copy command is supported by the document.queryCommandSupported method. We display the copy button if it’s supported. The button will call the copyToClipboard method if it’s clicked. In the method, we call the select method on the text area to select the text to copy. Then we run document.execCommand to run the copy command.

Conclusion

We can copy text to the clip with the copy command. Also, we can display things differently when our content is loading. Default prop values can be set with TypeScript and React. Thanks for reading my article, I hope you found it helpful!