Simple React callback function examples

I think of the React callback function as the parent component extending a baseball mitt for a child to throw in the specific details of an instance. Though, Jason Arnold says it better, technically speaking:

Instead of passing down a piece of the state to a child component, the parent can pass down a callback function. This callback function is run at a later time, usually through some interaction with the child component. The purpose of this callback function is to change a piece of the state that is a part of the parent component.

Jason Arnold, Callback Functions in React, Medium

Two examples. The first is a class component, below. Code based on a great Stephen Grider tutorial on Udemy.

Here’s the code:

//App.js

import React from 'react';
import SearchBar from './SearchBar';

class App extends React.Component {
	onTermSubmit = (term) => {
		console.log(term);
	};

	render() {
		return (
			<div className="ui container">
				<SearchBar onFormSubmit={this.onTermSubmit}/>
			</div>
		);
	}
}

export default App;

// SearchBar.js
import React from 'react';

class SearchBar extends React.Component {
	state = { term: '' };

onInputChange = event => {
	this.setState({term: event.target.value});
};

onFormSubmit = event => {
	event.preventDefault();
	this.props.onFormSubmit(this.state.term);
};

render() {
	return (
	<div className="search-bar ui segment">
		<form onSubmit={this.onFormSubmit} className="ui form">
			<div className="field">
				<label>video search</label>
				<input 
					type="text" 
					value={this.state.term} 
					onChange={this.onInputchange}
				/>
			</div>
		</form>
	</div>
	);
  }
}

export default SearchBar;

Example 2, involving TypeScript and functional components.

Full code on CodeSandbox, here.

Thanks for reading! Twitter @SaraHarvy.

Building a React modal with Formik, MUI TextField and Select

Illustration Sara Harvey-Patrick

I recently added a modal to a React app for an educational start-up. The modal offers users a text field to create a new TypeScript object, and selection of the new or existing objects from the database to display.

The feature looked something like this below (the code is here on CodeSandbox). I spun up a toy program to make sure I understood the process, since it was one of my bigger jobs during a front end development internship.

Jump to:
Modal with MUI and hooks
Use Formik, MUI text field to create object, add to select
Pass data from child to parent component, callback example



Components

React modal with MUI and hooks

Back to top

The user clicks “add dog,” and a modal opens to show a text field. The code below works, with the lines relevant to this section highlighted.

AddDogField.tsx

import { useState } from "react";
import { Dialog, DialogContent, TextField } from "@material-ui/core";
import { modalStyles, AddDogContainer, DogButton } from "./AddDogStyles";
import { useFormik } from "formik";

interface AddDogFieldProps {
  onCreateDog: (name: string) => Promise<void>;  

//setting the event type in an interface passed to const AddDogField
  handleClickOpen: () => any;
  handleClose: () => any;
}

interface DogFormValues {
  dogName: string;
}

export const AddDogField = (props: AddDogFieldProps) => {
  
// here, state values store the initial state of the modal
  const [modalOpen, setModalOpen] = useState(false);
  const formik = useFormik({
    initialValues: {
      dogName: ""
    },
    onSubmit: async (
      values: DogFormValues,
      { resetForm }: { resetForm: () => void }
    ) => {
      if (values.dogName.length > 0) {
        await props.onCreateDog(values.dogName);
        resetForm();
      }
    }
  });

// custom functions to open and close the modal  
function handleClickOpen() {
  setModalOpen(true);
  }

function handleClickClose() { setModalOpen(false); }

// styles come from the AddDogStyles file (on CodeSandbox) const classes = modalStyles(); return ( <AddDogContainer> // button to open modal <DogButton onClick={handleClickOpen}>Add dog</DogButton> // more info on the Dialog component from MUI <Dialog open={modalOpen} onClose={handleClose} classes={{ container: classes.modalPlacement, paper: classes.dialogPaper }} fullWidth={true} maxWidth={"xs"} > <DialogContent> <form onSubmit={formik.handleSubmit}> <TextField id="dogName" name="dogName" type="dogName" variant="standard" placeholder="Dog's name" fullWidth style={{ fontSize: "14px" }} value={formik.values.dogName} onChange={formik.handleChange} /> <DogButton type="submit" style={{ margin: "10px 0px" }}> Submit </DogButton> </form> // button to close modal <DogButton onClick={handleClose}>Close</DogButton> </DialogContent> </Dialog> </AddDogContainer> ); };

Helpful tutorials and docs on modals:
1. Build a Simple React Modal with React, Eden Ella — without hooks
2. Modal Components in React Using Custom Hooks, James Dietrich — hooks, toggle

Use React Formik, MUI text field to create object, add to select

Back to top

The user opens the modal, and sees a text field for entering a dog’s name. The steps are highlighted in yellow, extra notes are in blue, with some modal code removed for clarity.

Child: AddDogField.tsx

import { useState } from "react";
import { useFormik } from "formik";

// parent callback function called by props (more on this in the next section)
interface AddDogFieldProps {
  onCreateDog: (name: string) => Promise<void>;
}

interface DogFormValues {
  dogName: string;
}

export const AddDogField = (props: AddDogFieldProps) => {
// useFormik (good for simple forms) vs Formik, Jared Palmer on GitHub
  const formik = useFormik({
// initialValues (here, dogName), tells Formik which key of "values" to update, Formik
    initialValues: {
      dogName: ""
    },
// 2. The Formik onSubmit handler is passed the form value (the new dog's name), along with FormikBag methods and props
    onSubmit: async (
// The values are assigned a string type through DogFormValues
      values: DogFormValues,
// "either reset the form after submission, or imperatively reset the form," (Form Foundations). 
      { resetForm }: { resetForm: () => void }
    ) => {
      if (values.dogName.length > 0) {
// 3. onCreateDog() is a callback function defined in the parent component, DogList, and passed here via props (interface addDogFieldProps, above). More on this in the next section.
        await props.onCreateDog(values.dogName);
// examples of FormikBag: methods that begin with set, resetForm, any props passed to wrapped component
        resetForm();
      }
    }
  });

  return (
    <AddDogContainer>
      <DogButton onClick={handleClickOpen}>Add dog</DogButton
      <Dialog>
        <DialogContent>

          // 1. The user enters a name, and it triggers handleSubmit()
          <form onSubmit={formik.handleSubmit}>
            <TextField
              id="dogName"
              name="dogName"
              type="dogName"
              variant="standard"
              placeholder="Dog's name"
              fullWidth
              style={{ fontSize: "14px" }}
              value={formik.values.dogName}
              onChange={formik.handleChange}
            />
            <DogButton type="submit" style={{ margin: "10px 0px" }}>
              Submit
            </DogButton>
          </form>
          <DogButton onClick={() => handleClose()}>Close</DogButton>
        </DialogContent>
      </Dialog>
    </AddDogContainer>
  );
};

Pass data from React child to parent functional component, example

Back to top

Parent: DogList.tsx (full code)

import { useState } from "react";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import { AddDogField } from "./AddDogField";
import { DogListContainer, ListContainer } from "./AddDogStyles";

interface Dog {
  id: number;
  name: string;
}

export const DogList = () => {
  const [dogs, setDogs] = useState<Dog[]>([]);
  const [selectedDog] = useState<number | null>(null);
  const [modalOpen, setModalOpen] = useState(false);

  const handleClickOpen = () => {
    setModalOpen(true);
  };

  const handleClose = () => {
    setModalOpen(false);
  };

// 4. Returns the selected dog's name (the value of <MenuItem value={dog.name}>)
  const onSelectDog = (event: any) => {
    const selectedDogName = event.target.value;
    document.getElementById("showName").innerHTML = selectedDogName;
  };

// 1. callback function, onCreateDog(), is passed as a prop to the child component to get data from the child's form (the new dog's name) to update the parent's state. It creates a Dog object using the name, and saves it to "dogs." State variable "setDogs" calls the useState hook, which preserves the list of names between re-renders. 
  const onCreateDog = async (name: string) => {
    const newDog: Dog = { name };
    const newDogList = [...dogs, newDog];
    setDogs(newDogList);
  };

  return (
    <div>
      <DogListContainer>
        <ListContainer>
// 3. dogs.map returns a list of all dogs for selection 
          <Select value={selectedDog} onChange={onSelectDog}>
            {dogs.map((dog: Dog) => (
              <MenuItem value={dog.name}>{dog.name}</MenuItem>
            ))}
          </Select>
        </ListContainer>
// 2. passing the callback function to the child as a prop
        <AddDogField
          onCreateDog={onCreateDog}
        />
      </DogListContainer>
    </div>
  );
};

As a new React developer with an object oriented mindset, I thought of child components as complete programs plugged into the parent. Objects are powerful, and inheritance is automatic, right? It was hard to wrap my mind around data flow, writing code to pass data between the child and parent, and lifting state without Redux.

Passing data from functional child to parent

Importing a child component, like my <AddDogField /> , is the parent engaging with the child. And, here, the parent passes the onCreateDog callback function to the child as a prop, so the child’s data (the new dog’s name) can ultimately update the parent’s state: “A new Dog(name) is born.”

The callback function lifts state up (React docs) to the closest common ancestor (DogList) between the components that use or change this state (both parent DogList and child AddDogField). So, data is still flowing unidirectionally, a tenant of React, from parent to child, and to the view.

Unidirectional flow in React means that users trigger actions in the view, the actions update state, and changes flow back down to the child components and view. So, it’s generally best to update state in response to a DOM event like onClick, or an effect like useEffect.

This callback function is run at a later time, usually through some interaction with the child component. The purpose of this callback function is to change a piece of the state that is a part of the parent component.

Jason Arnold, Callback functions in React, Medium

Helpful posts on passing data between components:

1. Passing Data Between a Parent and Child in React, Jasmine Gump
2. Callback Functions in React, Jason Arnold
3. Unidirectional Data Flow in React, Flaviocopes


Thanks for reading! More about my front end development internship at Mucktracker.com, here. If this post can be improved, DM me on Twitter at @SaraHarvy.

Flex-box slider with Vanilla JS

It looks like this:

Flex-box slider with responsive images

Here’s the general flex-box layout:

The photo images (mySlides) have width: 100% and height: auto to let them grow and shrink relative to the size of their wrapper.

<div class="mySlides-wrapper">

  <div class="mySlides">
      <img src="image1.png" style="width:100%; height: auto">
  </div>

  <div class="mySlides">
       <img src="image2.png" style="width:100%; height: auto">
  </div>

</div>

I set the wrapper to width: 800px, as a reasonable responsive start size, and the wrapper’s contents to flex-grow: 2; so the slide images grow to fill the wrapper.

.mySlides-wrapper {
  align-items: center;
  flex-grow: 2;
  max-width: 600px;
}

The other necessary code for creating the simple layout was setting the slider section (slideshow-container) to justify-content: space-between.

.slideshow-container {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 10px 0px 15px;
}

Vanilla JS onclick slider arrows

Full JavaScript:

<script>
        var slideIndex = 1;
        showSlides(slideIndex);

        function plusSlides(n) {
          showSlides(slideIndex += n);
        }

        function showSlides(n) {
          var i;
          var slides = document.getElementsByClassName("mySlides");
          if (n > slides.length) {slideIndex = 1}    
          if (n < 1) {slideIndex = slides.length}
          for (i = 0; i < slides.length; i++) {
              slides[i].style.display = "none";
          }
          slides[slideIndex-1].style.display = "flex"; 
        }
</script>

The script first sets the page default to show slide 1. showSlides is defined below these first two lines of JavaScript, but works outside of regular top-to-bottom flow control because its binding is defined using function declaration Eloquent Javascript.

        var slideIndex = 1;
        showSlides(slideIndex);

A user’s click on a directional arrow triggers plusSides(n). So, the current slide (slideIndex) is added to n (either 1 or + -1, depending on the arrow clicked). += is an addition assignment operator, which adds an operand to a variable, and updates the variable with the result (MDM, addition assignment).

          <a class="prev" onclick="plusSlides(-1)"> < </a>
          <a class="next" onclick="plusSlides(1)"> > </a>
        function plusSlides(n) {
          showSlides(slideIndex += n);
        }

So, n updates to the new current slide position, and passes as a parameter to showSlides(slideIndex += n).

getElementsByClassName and length property

getElementsByClassName returns a live HTMLcollection object (“slides”) which, here, includes all the child elements with the class name, “mySlides.” The JS length property (w3Schools) returns the number of elements in the object, often used when looping through elements. (More on live vs. static lists from Abi Travers.)

function showSlides(n) {
          var i; // declaring variable i (here, still undefined)
          var slides = document.getElementsByClassName("mySlides");
          if (n > slides.length) {slideIndex = 1}
          if (n < 1) {slideIndex = slides.length} // function continues below

if (n > slides.length) {slideIndex = 1} determines, “if the new slide index is greater than the length of the array, restart at position 1.”

if (n < 1) {slideIndex = slides.length} determines, “If the current slide index is less than 1, it’s the last one in the collection.”

JS for loop, display one element in collection

for (i = 0; i < slides.length; i++) {
              slides[i].style.display = "none";
          }
          slides[slideIndex-1].style.display = "flex"; 
        }

Parameters of the for statement (loops and iteration, MDM):

for ([initialExpression]; [conditionExpression]; [incrementExpression])
  statement
for (i = 0; i < slides.length; i++) {

The above code determines, “the counter starts at 0, and checks if the counter is still less than the length of the collection at each pass of the loop. At each pass, the counter increments by 1.”

“For each pass, set the slide with the index of the counter to display: none,” making all slides invisible.

              slides[i].style.display = "none";
          }

“Make one slide visible (at a time), the slide with the current index.”

             slides[slideIndex-1].style.display = "flex"; 
        }

Full code for flex slider with Vanilla JS

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="styles.css" />
        <script src="myCode.js"></script>
        <title>flex slider</title>
    </head>
    <body>
      <div class="flex-container">

        <!-- flex object 1 -->
        <div class="content"><h1>Flex slider</h1>
        </div>

        <!-- flex object 2 -->
        <div class="slideshow-container">
          <a class="prev" onclick="plusSlides(-1)"> < </a>

          <div class="mySlides-wrapper">

            <div class="mySlides">
              <img src="kitten.png" style="width:100%; height: auto">
            </div>

            <div class="mySlides">
              <img src="grumpy-cat.jpg" style="width:100%; height: auto">
            </div>
          </div>

          <a class="next" onclick="plusSlides(1)"> > </a>

          </div>
        </div>
      </div>

    <script>
        var slideIndex = 1;
        showSlides(slideIndex);
    </script>

    </body>
</html>

CSS

html {
  font-size: calc(60% + 0.8vmin);
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: Helvetica, sans-serif;
}

body {
  min-height: 100vh;
  margin: 0;
  padding: 0;
}

h1 {
  font-size: 3em;
  color: white;
  line-height: 80%;
  margin: 0;
  padding: 25px;
  text-align: center;
}

.flex-container {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.content {
  background-color: #370617;
}

/* slider */
.mySlides {
  display: none;
}

.slideshow-container {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 10px 0px 15px;
}

/* next & previous buttons */
.prev, .next {
  cursor: pointer;
  padding: 80px 20px;
  color: #370617;
  font-size: 80px;
  font-weight: bold;
}

.mySlides-wrapper {
  align-items: center;
  max-width: 600px;
  flex-grow: 2;
}

@media (max-width: 800px) {
  h1 { font-size: 2.25em; }
  .prev, .next { font-size: 40px; }
}

Vanilla JavaScript

function plusSlides(n) {
          showSlides(slideIndex += n);
        }

        function showSlides(n) {
          var i;
          var slides = document.getElementsByClassName("mySlides");
          if (n > slides.length) {slideIndex = 1}    
          if (n < 1) {slideIndex = slides.length}
          for (i = 0; i < slides.length; i++) {
              slides[i].style.display = "none";
          }
          slides[slideIndex-1].style.display = "flex"; 
        }

This project was adapted for flex-box from the W3Schools tutorial, How to Create a Slideshow.

For a slightly more complex live version, visit https://sara-harvey.github.io/starling/, with the code on GitHub at https://github.com/Sara-Harvey/starling. If I can do anything to improve this post, send me a DM on Twitter, and let me know. @SaraHarvy

The Increment of Improvement

Learning to code takes time, like anything creative

Pliers, window, code and my version of a woodcut of a man using a nokogiri saw

I was hitting errors on a coding project one afternoon in June (2019) like a fly bouncing its face against a window over and over. I stopped when FedEx delivered our new vice grip pliers. We wanted to install the air conditioner in our bedroom, but a child guard blocked the window, and the pliers were our key to cool.

In New York City, you only need the guard if you have children, but these bars are impossible to remove without a special tool. You can’t ask the super to do it, because the bars are “permanent.” I was already sweating over my laptop, so I breaked to tackle the window.

I Googled a refresher YouTube on vice grip pliers. Then, I clamped the pinchers onto the millimeter-tall side of the first screw head, which was the only way to grip it. You could saw the screw heads off the guard bars, according to the Internet, but we wanted to avoid destroying anything. So, I twisted against the rusty threads.

Again and again, and again.

I’m learning to code through the Flatiron School’s online boot camp, and my biggest epiphany so far has been that programming is building in the tiniest increments. You write code, hit enter, the program says, “Error!” and you fix it over and over. Soon, you have a beautiful web app.

You learn how to draw, write, cook or do anything in increments, but coding can make the increment aspect painfully obvious. If you need to fudge a chowder, just add more clams, or corn or Manhattan, but a Sinatra app needs working Ruby code or, “Error!”

I’m learning to be patient with the increment.

That afternoon, I had been programming with the Nokogiri gem, named for a precise woodworking saw, to try to grab the correct HTML elements, over and over, in a web scrape. When the screws on the window budged, then turned until they dropped free into my hand, I went back to my code encouraged that miracles really do happen.

Though, I am pretty handy. When I was 10 or 11, I liked sawing boards and logs with my dad’s old hand saw because I knew that if I was just patient, I could divide any castoff thing in the woodpile clean in half. Right after I sawed through the knee of my jeans. (This is how I played. Is that weird?)

If you’re learning something hard, coding, spoken Mandarin, chowder-craft, amateur carpentry, have faith. Increments build.

On Twitter @SaraHarvy

New New Romantics Bot

The @NewNewRomBot1 is a Ruby Twitter bot that mashes up lyrics from Duran Duran, Spandau Ballet, Boy George, Flock of Seagulls and other acts from the New Romantic/New Wave early 80s. So far, it involves 100 songs.

It tweeted daily here, until 2022
https://twitter.com/NewNewRomBot1

Previously hosted on Heroku