Skip to content

Instantly share code, notes, and snippets.

@ifindev
Last active March 29, 2023 03:59
Show Gist options
  • Select an option

  • Save ifindev/8a4a09a013054b6a9e03af82572063c9 to your computer and use it in GitHub Desktop.

Select an option

Save ifindev/8a4a09a013054b6a9e03af82572063c9 to your computer and use it in GitHub Desktop.
Random React Test

This application should allow the user to update their username by inputting a custom value and clicking the button.

The Username component is finished and should not be changed, but the App component is missing parts. Finish the App component so that the Username component displays the inputted text when the button is clicked.

The App component should use the React.useRef Hook to pass the input to the Username component for the input element and for the Username component.

For example, if the user inputs a new username of "John Doe" and clicks the button, the div element with id root should look like this:

<div><button>Change Username</button><input type="text"><h1>John Doe</h1></div>

Starter Code:

// React is loaded and is available as React and ReactDOM
// imports should NOT be used
class Username extends React.Component {
  state = { value: "" };

  changeValue(value) {
    this.setState({ value });
  }

  render() {
    const { value } = this.state;
    return <h1>{value}</h1>;
  }
}

function App() {
  function clickHandler() {}

  return (
    <div>
      <button onClick={clickHandler}>Change Username</button>
      <input type="text" />
      <Username />
    </div>
  );
}

document.body.innerHTML = "<div id='root'></div>";
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

document.querySelector("input").value = "John Doe";
document.querySelector("button").click();
setTimeout(() => console.log(document.getElementById("root").innerHTML));

You have a GroceryApp component, which receives a list of products, each one with name and votes. The app should render an unordered list, with a list item for each product. Products can be upvoted or downvoted.

By appropriately using React state and props, implement the upvote/downvote logic. Keep the state in the topmost component, while the Product component should accept props.

For example, passing the following array as products prop to GroceryApp [{ name: "Oranges", votes: 0 }, { name: "Bananas", votes: 0 }] and clicking the '+' button next to the Oranges should result in HTML like:

Solution:

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";

const Product = ({ product, vote }) => {
  const [numVote, setNumVote] = useState(vote);
  const plus = () => {
    setNumVote(numVote + 1);
  };
  const minus = () => {
    setNumVote(numVote - 1);
  };
  return (
    <li>
      <span>{product}</span> - <span>votes: {numVote}</span>
      <button onClick={plus}>+</button> 
      <button onClick={minus}>-</button>
    </li>
  );
};

const GroceryApp = (props) => {
  let [products, setProducts] = React.useState(props.products);
  const onVote = (dir, index) => {
    // Update the products array accordingly ...
  };

  return (
    <ul>
      {products.map((product, index) => {
        return(
          <Product product={product.name} vote={product.votes} key={index}/>
        );
      })}
    </ul>
  );
};

document.body.innerHTML = "<div id='root'></div>";

ReactDOM.render(
  <GroceryApp
    products={[
      { name: "Oranges", votes: 0 },
      { name: "Bananas", votes: 0 }
    ]}
  />,
  document.getElementById("root")
);

let plusButton = document.querySelector("ul > li > button");
if (plusButton) {
  plusButton.click();
}
console.log(document.getElementById("root").outerHTML);

Image Gallery App

An image gallery is a set of images with corresponding remove buttons. This is the HTML code for a gallery with two images:

<div>
  <div class="image">
    <img src="https://goo.gl/kjzfbE">
    <button class="remove">X</button>
  </div>
  <div class="image">
    <img src="https://goo.gl/d2JncW">
    <button class="remove">X</button>
  </div>
</div>

Implement the ImageGallery component that accepts a links prop and renders the gallery described above so that the first item in the links prop is the src attribute of the first image in the gallery. It should also implement the following logic: When the button is clicked, the image that is in the same div as the button should be removed from the gallery.

For example, after the first image has been removed from the gallery above, it's HTML code should look like this:

<div>
  <div class="image">
    <img src="https://goo.gl/d2JncW">
    <button class="remove">X</button>
  </div>
</div>

My Solution (Reusing some concept from Full React Tutorial #13 from The Net Ninja):

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";

// React is loaded and is available as React and ReactDOM
// imports should NOT be used
const ImageGallery = ({ links }) => {
  const [images, setImages] = useState(links);

  const handleDelete = (imgLink) => {
    const newImageLists = images.filter((link) => link !== imgLink);
    setImages(newImageLists);
  };
  
  return (
    <div>
      {images.map((link, index) => {
        return (
          <div className="image" key={index}>
            <img src={link} />
            <button className="remove" onClick={() => handleDelete(link)}>
              X
            </button>
          </div>
        );
      })}
    </div>
  );
};

document.body.innerHTML = "<div id='root'> </div>";

const rootElement = document.getElementById("root");
const links = ["https://goo.gl/kjzfbE", "https://goo.gl/d2JncW"];
ReactDOM.render(<ImageGallery links={links} />, rootElement);
// document.querySelectorAll('.remove')[0].click();
console.log(rootElement.innerHTML);

Tests: 4 pass / 0 fail

  • Example case: Correct answer
  • ImageGallery will render the correct list: Correct answer
  • Remove a single image: Correct answer
  • Remove multiple images: Correct answer
  • Your score is 100%, perfect!

This is how to map an array props to a specific jsx element:

{products.map((product, key) => {
  return(
    <li key={key}>
      <span>{product.name}</span> - <span>votes: {product.votes}</span>
      <button>+</button><button>-</button>
    </li>
  );
})}
/*
Toggle Message:
The Message component contains an anchor element and a paragraph below the anchor. Rendering of the paragraph should be
toggled by clicking on the anchor element using the following logic:
- At the start, the paragraph should not be rendered.
- After a click, the paragraph should be rendered.
- After another click, the paragraph should not be rendered.
- Finish the Message component by implementing this logic.
*/
import React from "react";
const Message = () => {
const [isClick, setClick] = React.useState(false);
const handleClick = () => {
setClick(!isClick);
};
return (
<React.Fragment>
<a href="#" onClick={handleClick}>
Want to buy a new car?
</a>
{isClick && <p>Call +11 22 33 44 now!</p>}
</React.Fragment>
);
};
document.body.innerHTML = "<div id='root'> </div>";
const rootElement = document.getElementById("root");
ReactDOM.render(<Message />, rootElement);
console.log("Before click: " + rootElement.innerHTML);
document.querySelector("a").click();
console.log("After click: " + rootElement.innerHTML);
/*
Using contect to provide a global state value which can be used simply by contect provider. Without this, we have to pass the props value to all nested parent elements for a single child in a component.
*/
import React, {useEffect, useRef} from "react"
import ReactDOM from "react-dom"
const SecurityContext = React.createContext({ username: "", permissions: [] });
const ControlsComponent = (props) => {
return (
<SecurityContext.Provider value={{ username: props.username }}>
<LogoutWrapper></LogoutWrapper>
</SecurityContext.Provider>
);
};
const LogoutWrapper = (props) => {
var context = React.useContext(SecurityContext);
return (
<div>
<p>{context.username}</p>
<button>Click here to logout</button>
</div>
);
};
document.body.innerHTML = "<div id='root' />";
ReactDOM.render(<ControlsComponent username="James"/>, document.getElementById("root"));
setTimeout(() => console.log(document.getElementById("root").innerHTML));

Focusing an element when first rendered using useRef and useEffect Hooks. Source: https://blog.maisie.ink/react-ref-autofocus/

Finish the FocusableInput component so that the input element automatically receives focus on the first render. The component should use React Hooks.

import React, {useEffect, useRef} from "react"
import ReactDOM from "react-dom"

// React is loaded and is available as React and ReactDOM
// imports should NOT be used
const FocusableInput = (props) => {
  const inputElement = useRef(null);
  
  useEffect(() => {
    if(inputElement.current) {
      inputElement.current.focus();
    }
  }, []);
  // Write your code here
  return <input ref={inputElement}/>;
};

document.body.innerHTML = "<div id='root' />";
ReactDOM.render(<FocusableInput />, document.getElementById("root"));
setTimeout(() => console.log(document.getElementById("root").innerHTML));
import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
// React is loaded and is available as React and ReactDOM
// imports should NOT be used
const Form = () => {
const nameRef = useRef();
const emailRef = useRef();
const passwordRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
const name = nameRef.current.value;
const email = emailRef.current.value;
const password = passwordRef.current.value;
console.log(name, email, password);
};
return (
<React.Fragment>
<label>
Name:
<input placeholder="name" type="text" ref={nameRef} />
</label>
<label>
Email:
<input placeholder="email" type="text" ref={emailRef} />
</label>
<label>
Password:
<input placeholder="password" type="text" ref={passwordRef} />
</label>
<hr />
<button onClick={() => nameRef.current.focus()}>Focus Name Input</button>
<button onClick={() => emailRef.current.focus()}>
Focus Email Input
</button>
<button onClick={() => passwordRef.current.focus()}>
Focus Password Input
</button>
<hr />
<button onClick={handleSubmit}>Submit</button>
</React.Fragment>
);
};
document.body.innerHTML = "<div id='root'> </div>";
const rootElement = document.getElementById("root");
ReactDOM.render(<Form />, rootElement);
/*
Combining useState and useRef for a persist value.
*/
import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
// React is loaded and is available as React and ReactDOM
// imports should NOT be used
const Counter = () => {
const [count, setCount] = useState(0);
const id = useRef(null);
const clearInterval = () => {
window.clearInterval(id.current);
};
const startInterval = () => {
id.current = window.setInterval(() => {
setCount((c) => c + 1);
}, 1000);
};
useEffect(() => {
startInterval();
}, []);
const resetCounter = () => {
setCount(0);
startInterval();
};
return (
<div>
<h1>{count}</h1>
<button onClick={clearInterval}>Stop</button>
<button onClick={startInterval}>Start</button>
<button onClick={resetCounter}>Reset</button>
</div>
);
};
document.body.innerHTML = "<div id='root'> </div>";
const rootElement = document.getElementById("root");
ReactDOM.render(<Counter />, rootElement);
console.log("Before click: " + rootElement.innerHTML);
console.log("After click: " + rootElement.innerHTML);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment