Categories
JavaScript React

Basic Built-in React Hooks- useState and useEffect

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we’ll look at some basic built-in React hooks, including useState and useEffect .

useState

The useState hook lets us manage the internal state of a function component. It takes an initial value as an argument and returns an array with the current state and a function to update it.

It returns the initial state when the component is initially rendered.

We can pass in a function to update the value if the new value is computed using the previous state.

For example, we can write the following to update the value based on a previous one:

function App() {  
  const [count, setCount] = React.useState(0);  
  return (  
    <>  
      Count: {count}  
      <button onClick={() => setCount(0)}>Reset</button>  
      <button onClick={() => setCount(prevCount => prevCount - 1)}>  
        Decrement  
      </button>  
      <button onClick={() => setCount(prevCount => prevCount + 1)}>  
        Increment  
      </button>  
    </>  
  );  
}

In the code above, we have:

setCount(prevCount => prevCount - 1)}

and:

setCount(prevCount => prevCount + 1)}

which decrements the count and increment it respectively by passing in functions that take the previous count as the parameter and return the new count.

Otherwise, we can just pass in the new value to the state update function as we did in:

setCount(0)

useState doesn’t automatically merge update objects. We can replicate this behavior with the spread syntax:

function App() {  
  const [nums, setNums] = React.useState({});  
  return (  
    <>  
      <p>{Object.keys(nums).join(",")}</p>  
      <button  
        onClick={() =>  
          setNums(oldNums => {  
            const randomObj = { [Math.random()]: Math.random() };  
            return { ...oldNums, ...randomObj };  
          })  
        }  
      >  
        Click Me  
      </button>  
    </>  
  );  
}

In the code above, we have:

setNums(oldNums => {  
            const randomObj = { [Math.random()]: Math.random() };  
            return { ...oldNums, ...randomObj };  
          })

to create a randomObj object with a random number as the key and value, and we merge that into another object with the old value then return it.

Then we display it with:

Object.keys(nums).join(",")

by getting the keys and joining them together.

Lazy initial state

We can pass in a function to useState if we want to delay the setting of the initial state.

It’ll be ignored after the initial render if we pass in a function.

This is useful if the initial state is computed from some expensive operation.

For example, we can write:

function App() {  
  const [count, setCount] = React.useState(() => 0);  
  return (  
    <>  
      Count: {count}  
      <button onClick={() => setCount(() => 0)}>Reset</button>  
      <button onClick={() => setCount(prevCount => prevCount - 1)}>  
        Decrement  
      </button>  
      <button onClick={() => setCount(prevCount => prevCount + 1)}>  
        Increment  
      </button>  
    </>  
  );  
}

In the code above, we have:

React.useState(() => 0)

which is a function that just returns 0.

We’ll see the same results as before.

Bailing out of a state update

If we update a state hook with the same value as the current. React will skip updating the state without rendering the children or firing effects.

React uses Object.is() to compare the current and new states, which is close to the === operator except that +0 and -0 are treated to be not equal and NaN is equal to itself.

React may still render before bailing out but it won’t go deeper into the tree if it finds that the old and new values are the same.

useEffect

We can use the useEffect hook to do various operations that aren’t allowed inside the main body of the function component, which is anything outside the rendering phase.

Therefore, we can use this to do any mutations, subscriptions, setting timers, and other side effects.

It takes a callback to run the code.

We can return a function inside to run any cleanup code after each render and also when the component unmounts.

The callback passed into useEffect fires after layout and paint, during a deferred event.

This makes this suitable for running operations that shouldn’t block the browser from updating the screen.

Code that must be run synchronously can be put into the callback of the useLayoutEffect hook instead, which is the synchronous version of useEffect .

It’s guaranteed to fire before any new renders. React will always flush the previous render’s effects before starting a new update.

Conditionally firing an effect

We can pass in a second argument to useEffect with an array of values that requires an effect to be run when they change.

For example, we can use it to get data from an API on initial render as follows:

function App() {  
  const [joke, setJoke] = React.useState({});  
  useEffect(() => {  
    (async () => {  
      const response = await fetch("https://api.icndb.com/jokes/random");  
      const res = await response.json();  
      console.log(res);  
      setJoke(res);  
    })();  
  }, []);  
  return (  
    <>  
      <p>{joke.value && joke.value.joke}</p>  
    </>  
  );  
}

Passing an empty array as the second argument will stop it from loading in subsequent renders.

We can pass in a value to the array to watch the value in the array change and then run the callback function:

function App() {  
  const [joke, setJoke] = React.useState({});  
  const [id, setId] = React.useState(1);  
  useEffect(() => {  
    (async () => {  
      const response = await fetch(`https://api.icndb.com/jokes/${id}`));  
      const res = await response.json();  
      console.log(res);  
      setJoke(res);  
    })();  
  }, [id]);  
  return (  
    <>  
      <button onClick={() => setId(Math.ceil(Math.random() * 100))}>  
        Random Joke  
      </button>  
      <p>{joke.value && joke.value.joke}</p>  
    </>  
  );  
}

In the code above, when we click the Random Joke button, setId is called with a new number between 1 and 100. Then id changes, which triggers the useEffect callback to run.

Then joke is set with a new value, then the new joke is displayed on the screen.

Conclusion

useState and useEffect are the most basic built-in React hooks.

useState lets us update the state by passing in a function which returns the initial state, or passing in the initial state directly.

It returns an array with the current state and a function to update the state. The state update function returned takes in a value or a function that has the previous value as the parameter and returns the new value if it depends on the previous value.

useEffect lets us run any side effect, mutation, or anything outside of the rendering phase.

It takes a callback with the code to run as the first argument, and an array with the values to watch for changes for before the callback is run.

If an empty array is passed in, then the callback only runs on initial render.

Categories
JavaScript React

Rendering Arrays in React Components

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we’ll look at how to render lists in a React component.

Lists and Keys

We can transform lists into HTML by calling the array’s map method.

For example, if we want to display an array of numbers as a list, we can write:

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

  render() {  
    return (  
      <div>  
        {[1, 2, 3, 4, 5].map(num => (  
          <span>{num}</span>  
        ))}  
      </div>  
    );  
  }  
}

In the code above, we called map on the [1, 2, 3, 4, 5] array. In the map method’s callback, we returned a span with the number inside and we do the same for each element.

Then we get:

12345

displayed on the screen.

We can assign it to a variable and then pass it into the ReactDOM.render method as follows:

import React from "react";  
import ReactDOM from "react-dom";
const nums = [1, 2, 3, 4, 5].map(num => <span>{num}</span>);
const rootElement = document.getElementById("root");  
ReactDOM.render(nums, rootElement);

We’ll get the same items displayed.

Also, we can use the same code inside the render method:

import React from "react";  
import ReactDOM from "react-dom";

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

  render() {  
    const nums = [1, 2, 3, 4, 5].map(num => <span>{num}</span>);  
    return <div>{nums}</div>;  
  }  
}

const rootElement = document.getElementById("root");  
ReactDOM.render(<App />, rootElement);

Keys

When we render lists, we should provide a value for key prop for each rendered element so that React can identify which items have changed, added, or removed.

It gives elements a stable identity. We should pick a key by using a string that uniquely identifies a list item among its siblings.

A key should be a string value.

For example, if we want to render a list of to-do items, we should pick the id field as the key ‘s value as follows:

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = {  
      todos: [  
        { id: 1, text: "eat" },  
        { id: 2, text: "drink" },  
        { id: 3, text: "sleep" }  
      ]  
    };  
  } 

  render() {  
    return (  
      <div>  
        {this.state.todos.map(todo => (  
          <p key={todo.id.toString()}>{todo.text}</p>  
        ))}  
      </div>  
    );  
  }  
}

In the code above, we have the key={todo.id.toString()} prop to set the key to the todo ‘s id converted to a string.

If we have no stable identity for our items, we can use the index for the entry as a last resort:

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = {  
      todos: [{ text: "eat" }, { text: "drink" }, { text: "sleep" }]  
    };  
  } 

  render() {  
    return (  
      <div>  
        {this.state.todos.map((todo, index) => (  
          <p key={index.toString()}>{todo.text}</p>  
        ))}  
      </div>  
    );  
  }  
}

index is always available and it’s unique for each array element, so it can be used as a value for the key prop.

Extracting Components with Keys

If we render components, we should put the key prop in the component rather than the element that’s being rendered.

For example, the following is incorrectly using the key prop:

function TodoItem({ todo }) {  
  return <p key={todo.id.toString()}>{todo.text}</p>;  
}

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = {  
      todos: [  
        { id: 1, text: "eat" },  
        { id: 2, text: "drink" },  
        { id: 3, text: "sleep" }  
      ]  
    };  
  } 

  render() {  
    return (  
      <div>  
        {this.state.todos.map(todo => (  
          <TodoItem todo={todo} />  
        ))}  
      </div>  
    );  
  }  
}

In the TodoItem component, we have:

<p key={todo.id.toString()}>{todo.text}</p>;

with the key prop. We don’t want this there because we don’t need to identify a unique li since it’s isolated from the outside already. Instead, we want to identify a unique TodoItem .

Instead, we should write:

function TodoItem({ todo }) {  
  return <p>{todo.text}</p>;  
}class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = {  
      todos: [  
        { id: 1, text: "eat" },  
        { id: 2, text: "drink" },  
        { id: 3, text: "sleep" }  
      ]  
    };  
  } 

  render() {  
    return (  
      <div>  
        {this.state.todos.map(todo => (  
          <TodoItem todo={todo} key={todo.id.toString()} />  
        ))}  
      </div>  
    );  
  }  
}

We should add keys to items return with map ‘s callback.

Keys Only Need to Be Unique Among Siblings

Keys only need to be unique among sibling elements.

For example, we can write:

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = {  
      posts: [  
        { id: 1, text: "eat" },  
        { id: 2, text: "drink" },  
        { id: 3, text: "sleep" }  
      ],  
      comments: [  
        { id: 1, text: "eat" },  
        { id: 2, text: "drink" },  
        { id: 3, text: "sleep" }  
      ]  
    };  
  } 

  render() {  
    return (  
      <div>  
        <div>  
          {this.state.posts.map(post => (  
            <p key={post.id.toString()}>{post.text}</p>  
          ))}  
        </div>  
        <div>  
          {this.state.comments.map(comment => (  
            <p key={comment.id.toString()}>{comment.text}</p>  
          ))}  
        </div>  
      </div>  
    );  
  }

Since posts and comments aren’t rendered in the same div , the key values can overlap.

The key prop doesn’t get passed to components. If we need the same value in a component we have to use a different name.

Embedding map() in JSX

We can embed the expression that calls map straight between the curly braces.

For example, we can write:

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

  render() {  
    return (  
      <div>  
        {[1, 2, 3, 4, 5].map(num => (  
          <span key={num.toString()}>{num}</span>  
        ))}  
      </div>  
    );  
  }  
}

Conclusion

We can use JavaScript array’s map method to map values to React elements or components.

To help React identify elements unique, we should pass in a unique string to key prop for each entry rendered.

It should be passed to anything that’s returned by map ‘s callback since they’re the items that have to be uniquely identified by React.

Also, keys only have to be unique among direct siblings. Other places can have the same key values.

Categories
JavaScript React

Basic Built-in React Hooks- useState and useEffect

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we’ll look at some basic built-in React hooks, including useState and useEffect .

useState

The useState hook lets us manage the internal state of a function component. It takes an initial value as an argument and returns an array with the current state and a function to update it.

It returns the initial state when the component is initially rendered.

We can pass in a function to update the value if the new value is computed using the previous state.

For example, we can write the following to update the value based on a previous one:

function App() {  
  const [count, setCount] = React.useState(0);  
  return (  
    <>  
      Count: {count}  
      <button onClick={() => setCount(0)}>Reset</button>  
      <button onClick={() => setCount(prevCount => prevCount - 1)}>  
        Decrement  
      </button>  
      <button onClick={() => setCount(prevCount => prevCount + 1)}>  
        Increment  
      </button>  
    </>  
  );  
}

In the code above, we have:

setCount(prevCount => prevCount - 1)}

and:

setCount(prevCount => prevCount + 1)}

which decrements the count and increment it respectively by passing in functions that take the previous count as the parameter and return the new count.

Otherwise, we can just pass in the new value to the state update function as we did in:

setCount(0)

useState doesn’t automatically merge update objects. We can replicate this behavior with the spread syntax:

function App() {  
  const [nums, setNums] = React.useState({});  
  return (  
    <>  
      <p>{Object.keys(nums).join(",")}</p>  
      <button  
        onClick={() =>  
          setNums(oldNums => {  
            const randomObj = { [Math.random()]: Math.random() };  
            return { ...oldNums, ...randomObj };  
          })  
        }  
      >  
        Click Me  
      </button>  
    </>  
  );  
}

In the code above, we have:

setNums(oldNums => {  
            const randomObj = { [Math.random()]: Math.random() };  
            return { ...oldNums, ...randomObj };  
          })

to create a randomObj object with a random number as the key and value, and we merge that into another object with the old value then return it.

Then we display it with:

Object.keys(nums).join(",")

by getting the keys and joining them together.

Lazy initial state

We can pass in a function to useState if we want to delay the setting of the initial state.

It’ll be ignored after the initial render if we pass in a function.

This is useful if the initial state is computed from some expensive operation.

For example, we can write:

function App() {  
  const [count, setCount] = React.useState(() => 0);  
  return (  
    <>  
      Count: {count}  
      <button onClick={() => setCount(() => 0)}>Reset</button>  
      <button onClick={() => setCount(prevCount => prevCount - 1)}>  
        Decrement  
      </button>  
      <button onClick={() => setCount(prevCount => prevCount + 1)}>  
        Increment  
      </button>  
    </>  
  );  
}

In the code above, we have:

React.useState(() => 0)

which is a function that just returns 0.

We’ll see the same results as before.

Bailing out of a state update

If we update a state hook with the same value as the current. React will skip updating the state without rendering the children or firing effects.

React uses Object.is() to compare the current and new states, which is close to the === operator except that +0 and -0 are treated to be not equal and NaN is equal to itself.

React may still render before bailing out but it won’t go deeper into the tree if it finds that the old and new values are the same.

useEffect

We can use the useEffect hook to do various operations that aren’t allowed inside the main body of the function component, which is anything outside the rendering phase.

Therefore, we can use this to do any mutations, subscriptions, setting timers, and other side effects.

It takes a callback to run the code.

We can return a function inside to run any cleanup code after each render and also when the component unmounts.

The callback passed into useEffect fires after layout and paint, during a deferred event.

This makes this suitable for running operations that shouldn’t block the browser from updating the screen.

Code that must be run synchronously can be put into the callback of the useLayoutEffect hook instead, which is the synchronous version of useEffect .

It’s guaranteed to fire before any new renders. React will always flush the previous render’s effects before starting a new update.

Conditionally firing an effect

We can pass in a second argument to useEffect with an array of values that requires an effect to be run when they change.

For example, we can use it to get data from an API on initial render as follows:

function App() {  
  const [joke, setJoke] = React.useState({});  
  useEffect(() => {  
    (async () => {  
      const response = await fetch("https://api.icndb.com/jokes/random");  
      const res = await response.json();  
      console.log(res);  
      setJoke(res);  
    })();  
  }, []);  
  return (  
    <>  
      <p>{joke.value && joke.value.joke}</p>  
    </>  
  );  
}

Passing an empty array as the second argument will stop it from loading in subsequent renders.

We can pass in a value to the array to watch the value in the array change and then run the callback function:

function App() {  
  const [joke, setJoke] = React.useState({});  
  const [id, setId] = React.useState(1);  
  useEffect(() => {  
    (async () => {  
      const response = await fetch(`https://api.icndb.com/jokes/${id}`);  
      const res = await response.json();  
      console.log(res);  
      setJoke(res);  
    })();  
  }, [id]);  
  return (  
    <>  
      <button onClick={() => setId(Math.ceil(Math.random() * 100))}>  
        Random Joke  
      </button>  
      <p>{joke.value && joke.value.joke}</p>  
    </>  
  );  
}

In the code above, when we click the Random Joke button, setId is called with a new number between 1 and 100. Then id changes, which triggers the useEffect callback to run.

Then joke is set with a new value, then the new joke is displayed on the screen.

Conclusion

useState and useEffect are the most basic built-in React hooks.

useState lets us update the state by passing in a function which returns the initial state, or passing in the initial state directly.

It returns an array with the current state and a function to update the state. The state update function returned takes in a value or a function that has the previous value as the parameter and returns the new value if it depends on the previous value.

useEffect lets us run any side effect, mutation, or anything outside of the rendering phase.

It takes a callback with the code to run as the first argument, and an array with the values to watch for changes for before the callback is run.

If an empty array is passed in, then the callback only runs on initial render.

Categories
JavaScript React

Watching MobX Observables with the Reaction Function

We can create Observable objects with MobX. To watch it for changes, we can use the reaction function.

In this article, we’ll look at how to use the reaction function to watch for value changes in MobX Observables.

Reaction

We can use the reaction function as follows:

reaction(() => data, (data, reaction) => { sideEffect }, options?)

The reaction function is a variation on autorun that gives more fine-grained control on how an observable will be tracked.

The side-effect won’t be run directly when the observable is created. It’ll run only after the data expression returns a new value for the first time.

Any observables that are accessed while executing the side effect won’t be tracked.

reaction returns a disposer function.

The second function passed to reaction will retrieve 2 arguments when it’s run. The first argument is the value returned by the data function. The second argument is the current reaction during execution.

The side effect only reacts to data that was accessed in the data expression. It’ll only be triggered when the data returned by the expression has changed. This means that we have to produce things we need on our side effects.

Options

The third argument of reaction is an options object that can take the following optional options:

  • fireImmediately — boolean that indicates the effect function should trigger immediately after the first run of the data function. This is false by default.
  • delay — number of milliseconds that can be used to debounce the effect function. If it’s zero then no debouncing will happen
  • equals — a compare function that’ll be used to compare the previous and next values produced by the data function. The effect function will only be invoked if the function returns false . Default is comparer.default .
  • name — a string that’s used as the name for the reaction
  • onError — a function that’ll handle the errors of this reaction, rather than propagating them
  • scheduler — set a custom scheduler to determine how re-running the autorun function should be scheduled

Usage

We can use the reaction function as follows:

import { reaction, observable } from "mobx";
const todos = observable([  
  {  
    title: "eat"  
  },  
  {  
    title: "drink"  
  }  
]);

reaction(  
  () => todos.map(todo => todo.title),  
  titles => console.log(titles.join(", "))  
);

todos.push({ title: "sleep" });

In the code above, we created a new observable array using the observable function, which we assigned to the todos constant.

Then we call the reaction function, where we pass a callback to return an array which has the title strings from the todos observable array as the first argument.

In the second argument, we get the titles array returned from the function in the first argument, and the console.log the titles array joined together with join .

Then we call push on it, as we did in the last line, the new comma-separated string will be logged with the console.log since we changed it. It won’t log the value when it’s initially created.

Conclusion

We can use the reaction function to watch for observable variable changes.

It won’t watch for the value when it’s initially created.

reaction takes a callback to watch the value and return something we want from it. The second argument takes a value that takes the returned value from the first function and then we can perform some side effects on it.

The 3rd argument takes an object that takes a variety of options.

Categories
JavaScript React

Lifting State in React Components

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we’ll look at the principle of lifting states up.

Lifting State Up

We should lift any shared state up to their closest common ancestor.

This way, one state can be shared between multiple child components by passing them down via props.

For example, if we want to build a calculator for converting lengths, we can write the following:

import React from "react";  
import ReactDOM from "react-dom";

class LengthInput extends React.Component {  
  constructor(props) {  
    super(props);  
    const { length } = this.props;  
    this.state = { length };  
  }  
  
  handleChange(e) {  
    this.props.onChange(e);  
  } 

  componentWillReceiveProps(props) {  
    const { length } = props;  
    this.setState({ length });  
  } 

  render() {  
    const { unit } = this.props;  
    return (  
      <>  
        <label>Length ({unit})</label>  
        <input  
          value={this.state.length}  
          onChange={this.handleChange.bind(this)}  
          placeholder={this.props.unit}  
        />  
        <br />  
      </>  
    );  
  }  
}

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = { lengthInMeter: 0, lengthInFeet: 0 };  
  } 

  handleMeterChange(e) {  
    this.setState({ lengthInMeter: +e.target.value });  
    this.setState({ lengthInFeet: +e.target.value \* 3.28 });  
  } 

  handleFeetChange(e) {  
    this.setState({ lengthInFeet: +e.target.value });  
    this.setState({ lengthInMeter: +e.target.value / 3.28 });  
  } 

  render() {  
    return (  
      <div className="App">  
        <LengthInput  
          length={this.state.lengthInMeter}  
          onChange={this.handleMeterChange.bind(this)}  
          unit="meter"  
        />  
        <LengthInput  
          length={this.state.lengthInFeet}  
          onChange={this.handleFeetChange.bind(this)}  
          unit="feet"  
        />  
      </div>  
    );  
  }  
}

const rootElement = document.getElementById("root");  
ReactDOM.render(<App />, rootElement);

In the code above, we have a length converter than converts meters to feet when we’re typing in the meters field and vice versa.

What we’re doing is that we keep the lengths all in the App component. This is why the principle is called lifting the states up.

Since we’re keeping the lengths in the App component, we have to pass them down to the LengthInput components.

To do that, we pass props to them. Also, we pass in the units, and the change handler functions down to our LengthInput components, so that they can the functions to update the states in the App component.

In the handleFeetChange and handleMeterChange functions, we set the state according to the values entered in the LengthInput components.

We call this.setState in both functions to set the states. Each time setState is called, render will be called, so that the latest state will be passed down to our LengthInput components.

In the LengthInput components, we have the componentWillReceiveProps hook which will get the latest prop values and then set the length state accordingly.

this.state.length is set as the value of the input elements in LengthInput , so the inputted value will be shown.

The advantage of lifting the states up to the parent component is that we only have to keep one copy of the state. Also, we don’t have to repeat the processing of the states in different child components.

The values of the inputs stay in sync because they’re computed from the same state.

Conclusion

When writing React apps, it’s recommended that we move shared states between child components into their parent component. This is called lifting the states up.

We want to do this so that we don’t have to duplicate the processing of data in child components, and we only have a single source of truth.

To do this, we can pass in data and function as props down to child components. This way, functions from the parent component can be called from child components.

Then to update the states of child component with the latest values passed from the props, we can use the componentWillReceiveProps hook to update the states from the props.