Converting Vanilla JavaScript to React

Making the MixIt app into MixItRemix, a React app, without using Router (Part 2: Building the sidebar navigation and rendering the forms)

Alie Brubaker
4 min readAug 2, 2022

It bears repeating that the Router hook would have improved the next portion of the app significantly, though the intention of this conversion was to mimic the original app as much as possible.

Bringing back up our component hierarchy, we see that the header is the home of three of our sidebar options. Only the random cocktail navigation button bypasses the header but it shares extremely similar functionality with the forms for name or main ingredient and the first letter dropdown menu, so I decided that the RandomCocktail component should be a child of Header, too.

I’m getting ahead of myself, though. First we need to look at the Sidebar component or more correctly the SidebarOpen subcomponent and compare it with our vanilla code.

Our plan for creating the various forms appear in the header was to use a display tag in their selectors of the stylesheet set to none. Then as, the user clicked each sidebar option it would simply make the relevant menu appear. To that end, we gave all of the search forms a className of search and set their display to none.

.search {height: 25px;display: none;}

Then in our JS, we did the same thing we did with the toggleNav function. We changed their styling to reflect a display of block, which made the form appear in the header.

cocktailName.addEventListener('click', (e) => {hideForms();nameSearchForm.style.display = 'block';})mainIngredient.addEventListener('click', (e) => {hideForms();ingredientSearchForm.style.display = 'block';})firstLetter.addEventListener('click', () => {drinksListContainer.style.display = '';hideForms();populateLetterDropdown();letterDropdown.style.display = 'block';})letterDropdown.addEventListener('change', (e) => {drinksListContainer.innerHTML = ''drinksListContainer.style.display = 'block'detailContainer.style.width = '50%'fetchRequest(byLetter, e.target.value).then(renderList)})

In one way, the conversion to React was significantly better than this but it came with the same drawback as the sidebar sliding open. In React, each menu form renders or unmounts as the user selects different options. This is obviously better from a resources standpoint as all three menus wouldn’t need to load with the page. Still, the issue of the buttons for the sidebar menu living in the Sidebar component and needing to interact with the Header component meant that I had to pass up additional state with a callback toApp, selectForm, to store the choice the user made so it could be passed back down to Header to render the selected form.

const SidebarOpen = ({onFormSelect}) => {const handleOpenFormClick = (e) => {onFormSelect(e);}return (<ul><li><span>Search By:</span></li><li><buttononClick={handleOpenFormClick}id='cocktail-name'name="cocktailName">Cocktail Name</button></li><li><buttononClick={handleOpenFormClick}id='first-letter'name="firstLetter">First Letter of Cocktail</button></li><li><buttononClick={handleOpenFormClick}id='main-ingredient'name="mainIngredient">Drink Ingredient</button></li><li><buttononClick={handleOpenFormClick}id='random-cocktail'name="randomCocktail">Random Cocktail</button></li></ul>)}

I followed that with the Header passing down the callback to App to each relevant form as it renders.

const Header = ({onDrinkSelection,onFetchSubmission,toggleNavStatus,selectForm}) => {const handleRenderForm = () => {switch (selectForm) {case "cocktailName":return <NameForm onFetchSubmission={onFetchSubmission}/>;case "firstLetter":return <FirstLetterForm onFetchSubmission={onFetchSubmission}/>;case "mainIngredient":return <MainIngredientForm onFetchSubmission={onFetchSubmission}/>;case "randomCocktail":return <RandomCocktail onDrinkSelection={onDrinkSelection}/>;}}return (<header className={toggleNavStatus ? "main-sidebar-open" : "main" }><h1>Mix It Remix</h1>{handleRenderForm()}</header>)}export default Header;

Obviously, another drawback was that it required many more lines of code in the React version compared with the vanilla JavaScript. I foster no illusions that this will be the case for much larger and more complicated applications and sincerely doubt that it is.

Within each form component, there is a single input form that, upon submission, sends the user input as the argument to the callback function declared in App. I wanted the fetch request to occur in the submit event handler before the callback was invoked to pessimistically render the list of drinks just as it did in the vanilla version.

const NameForm = ({onFetchSubmission}) => {const [nameForm, setNameForm] = useState("");const handleNameFormChange = (e) => {setNameForm(e.target.value);}const handleNameFormSubmit = (e) => {e.preventDefault();getFormSubmission()}const getFormSubmission = () => {fetch(`https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${nameForm.toLowerCase()}`).then(r => r.json()).then(newSubmission => {onFetchSubmission(newSubmission.drinks)})}
return(<formonSubmit={handleNameFormSubmit}className='search'id='search-name'><inputonChange={handleNameFormChange}type='text'value={nameForm}name='input'placeholder='Enter Drink Name'/><input type='submit'/></form>)}export default NameForm;

However, neither the FirstLetter nor the RandomCocktail component had a submit and instead I imported the useEffect hook to fetch and immediately render the list of drinks or random drink respectively.

const RandomCocktail = ({onDrinkSelection}) => {useEffect(() => {fetch(`https://www.thecocktaildb.com/api/json/v1/1/random.php`).then(r => r.json()).then(newSubmission => {onDrinkSelection(newSubmission.drinks[0])})},[])return (<></>)}export default RandomCocktail;

So we are left with a hierarchy that looks like this.

A schematic depicting the component hierarchy of a React application
The second step: using the selection in the sidebar to render the form

All we need now is the render the DrinksList and the DrinkDetails .

--

--

Alie Brubaker

I’m a 36 year old Canadian transgender recent Flatiron School Software Engineering bootcamp graduate looking for work