From 7fe78630b615741f6356c9a36b2161857aff94f5 Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Thu, 18 Jan 2024 14:51:39 -0500 Subject: [PATCH 01/15] refactor: phone-book.js solution --- solutions/phone-book.js | 51 +++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/solutions/phone-book.js b/solutions/phone-book.js index 67b94bf..c5b73d0 100644 --- a/solutions/phone-book.js +++ b/solutions/phone-book.js @@ -1,5 +1,5 @@ -import React, { useState, useReducer } from "react" -import ReactDOM from "react-dom" +import React, { useState, useReducer, useCallback } from "react" +import { createRoot } from "react-dom/client" const style = { table: { @@ -34,7 +34,7 @@ const style = { }, } -function PhoneBookForm({ addEntryToPhoneBook }) { +function PhoneBookForm({ entries, setEntries }) { const initialFormState = { userFirstname: "Coder", userLastname: "Byte", @@ -55,6 +55,24 @@ function PhoneBookForm({ addEntryToPhoneBook }) { const onChange = ({ target: { name, value } }) => dispatch({ type: name, payload: value }) + const addEntryToPhoneBook = useCallback(() => { + const { userFirstname, userLastname, userPhone } = formState + const newEntries = [ + ...entries, + { userFirstname, userLastname, userPhone }, + ] + const newSortedEntries = newEntries.sort((a, b) => { + const userLastnameA = a.userLastname.toLowerCase() + const userLastnameB = b.userLastname.toLowerCase() + return userLastnameA < userLastnameB + ? -1 + : userLastnameA > userLastnameB + ? 1 + : 0 + }) + setEntries(newSortedEntries) + }, [formState]) + return (
{ @@ -132,34 +150,13 @@ function InformationTable({ entries }) { function Application() { const [entries, setEntries] = useState([]) - - const addEntryToPhoneBook = ({ - userFirstname, - userLastname, - userPhone, - }) => { - const newEntries = [ - ...entries, - { userFirstname, userLastname, userPhone }, - ] - const newSortedEntries = newEntries.sort((a, b) => { - const userLastnameA = a.userLastname.toLowerCase() - const userLastnameB = b.userLastname.toLowerCase() - return userLastnameA < userLastnameB - ? -1 - : userLastnameA > userLastnameB - ? 1 - : 0 - }) - setEntries(newSortedEntries) - } - return (
- +
) } -ReactDOM.render(, document.getElementById("root")) +const root = createRoot(document.getElementById("root")) +root.render() From 4f1e5808a675efa6c6026f515cc6c30924ecb4c9 Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Thu, 18 Jan 2024 15:05:24 -0500 Subject: [PATCH 02/15] refactor: simple-counter.js --- solutions/simple-counter.js | 45 +++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/solutions/simple-counter.js b/solutions/simple-counter.js index c4c92b4..ebea834 100644 --- a/solutions/simple-counter.js +++ b/solutions/simple-counter.js @@ -1,30 +1,25 @@ -import React, { useState } from 'react'; -import ReactDOM from 'react-dom'; +import React, { useState } from "react" +import { createRoot } from "react-dom/client" const Counter = () => { - const [count, setCount] = useState(0); + const [count, setCount] = useState(0) - increment = () => { - setCount(prevCount => prevCount + 1); - }; + const increment = () => { + setCount(prevCount => prevCount + 1) + } - return ( -
-

- Button Count: - {count} -

- -
- ); -}; + return ( +
+

+ {`Button Count: `} + {count} +

+ +
+ ) +} -ReactDOM.render( - , - document.getElementById('root') -); \ No newline at end of file +const root = createRoot(document.getElementById("root")) +root.render() From 5fa3a960d946a3bc00874eafc3ab56d99b785f80 Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Thu, 18 Jan 2024 15:14:31 -0500 Subject: [PATCH 03/15] refactor: button-toggle.js --- solutions/button-toggle.js | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/solutions/button-toggle.js b/solutions/button-toggle.js index d530905..b75f0f1 100644 --- a/solutions/button-toggle.js +++ b/solutions/button-toggle.js @@ -1,22 +1,19 @@ -import React, { useState } from 'react'; -import ReactDOM from 'react-dom'; +import React, { useState } from "react" +import { createRoot } from "react-dom/client" const Toggle = () => { + const [toggle, setToggle] = useState(false) - const [toggle, setToggle] = useState(false); - - const handleClick = () => { - setToggle(!toggle); - }; + const handleClick = () => { + setToggle(!toggle) + } return ( - - ); -}; + + ) +} -ReactDOM.render( - , - document.getElementById('root') -); \ No newline at end of file +const root = createRoot(document.getElementById("root")) +root.render() From 1744da67daf9b18ca3603224fbbfee31608ebf3f Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Thu, 18 Jan 2024 16:07:47 -0500 Subject: [PATCH 04/15] refactor: list.js --- solutions/list.js | 58 ++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/solutions/list.js b/solutions/list.js index 820a0d2..432df61 100644 --- a/solutions/list.js +++ b/solutions/list.js @@ -1,43 +1,33 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; +import React from "react" +import { createRoot } from "react-dom/client" const data = [ - { name: 'Daniel', age: 25 }, - { name: 'John', age: 24 }, - { name: 'Jen', age: 31 }, -]; + { name: "Daniel", age: 25 }, + { name: "John", age: 24 }, + { name: "Jen", age: 31 }, +] -const DataItem = ({ name, age}) => { - - return ( +const DataItem = ({ name, age }) => (
  • - {name}{` `} - {age} + {`${name} `} + {age}
  • - ); -}; +) -const DataList = () => { - - return ( +const DataList = ({ data }) => (
    -

    Data List

    -
      - {data.map((dataItem, index) => { - return ( - - ); - })} -
    +

    Data List

    +
      + {data.map((dataItem, index) => ( + + ))} +
    - ); -}; +) -ReactDOM.render( - , - document.getElementById('root') -); \ No newline at end of file +const root = createRoot(document.getElementById("root")) +root.render() From 32767c3fd0f134afcaecebd383bd75ebfe27c1e5 Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Thu, 18 Jan 2024 16:56:49 -0500 Subject: [PATCH 05/15] refactor: context-api.js --- solutions/context-api.js | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/solutions/context-api.js b/solutions/context-api.js index ef025b6..ed846af 100644 --- a/solutions/context-api.js +++ b/solutions/context-api.js @@ -1,5 +1,5 @@ import React, { useState, createContext, useContext } from "react" -import ReactDOM from "react-dom" +import { createRoot } from "react-dom/client" const languages = ["JavaScript", "Python"] const LanguageContext = createContext({ @@ -8,25 +8,14 @@ const LanguageContext = createContext({ setLanguage: () => {}, }) -function App() { - const [language, setLanguage] = useState(languages[0]) - return ( - - - - ) -} - -function MainSection() { +const MainSection = () => { const { languages, language, setLanguage } = useContext(LanguageContext) const currentIndex = languages.indexOf(language) - const toggleLanguage = () => { - if (currentIndex === languages.length - 1) { - setLanguage(languages[0]) - } else { - setLanguage(languages[currentIndex + 1]) - } - } + const toggleLanguage = () => + currentIndex === languages.length - 1 + ? setLanguage(languages[0]) + : setLanguage(languages[currentIndex + 1]) + return (

    {`Favorite programming language: ${language}`}

    @@ -37,4 +26,14 @@ function MainSection() { ) } -ReactDOM.render(, document.getElementById("root")) +const App = () => { + const [language, setLanguage] = useState(languages[0]) + return ( + + + + ) +} + +const root = createRoot(document.getElementById("root")) +root.render() From ef699b62ea5f4e5457199d5e9919caf99b1bb2fb Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Thu, 18 Jan 2024 17:25:29 -0500 Subject: [PATCH 06/15] refactor: typescript-button-toggle.ts --- solutions/typescript-button-toggle.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/solutions/typescript-button-toggle.ts b/solutions/typescript-button-toggle.ts index 618fc2e..7cc1f07 100644 --- a/solutions/typescript-button-toggle.ts +++ b/solutions/typescript-button-toggle.ts @@ -1,18 +1,15 @@ import React, { useState, ReactNode } from "react" -import ReactDOM from "react-dom" +import { createRoot } from "react-dom/client" -function Toggle(): ReactNode { +const Toggle: ReactNode = () => { const [toggle, setToggle] = useState(true) - function onClick(): void { + const onClick = (): void => { setToggle(!toggle) } - return ( - - ) + return } -ReactDOM.render(, document.getElementById("root")) +const root = createRoot(document.getElementById("root")) +root.render() From eb4a64ddc11fa2be90e074ce6a816ff925bf0148 Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Thu, 18 Jan 2024 17:53:52 -0500 Subject: [PATCH 07/15] refactor: react-native-simple-counter.js --- solutions/react-native-simple-counter.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/solutions/react-native-simple-counter.js b/solutions/react-native-simple-counter.js index 26c4822..c6b6dbe 100644 --- a/solutions/react-native-simple-counter.js +++ b/solutions/react-native-simple-counter.js @@ -1,5 +1,5 @@ import React, { useState } from "react" -import { Text, View, StyleSheet, Button } from "react-native" +import { Text, View, StyleSheet } from "react-native" const SimpleCounter = () => { const [count, setCount] = useState(0) @@ -9,11 +9,11 @@ const SimpleCounter = () => { return ( - button count: {count} + button count: {count} - + ) } @@ -26,7 +26,7 @@ const styles = StyleSheet.create({ }, counter: { textAlign: "center", - marginVertical: 10, + marginVertical: 20, }, }) From 0f23e6a04ef7be236886c47c54e7a9ea14ad9585 Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Sat, 20 Jan 2024 01:16:05 -0500 Subject: [PATCH 08/15] refactor: tic-tac-toe.js --- solutions/tic-tac-toe.js | 424 ++++++++++++++++----------------------- 1 file changed, 169 insertions(+), 255 deletions(-) diff --git a/solutions/tic-tac-toe.js b/solutions/tic-tac-toe.js index 38289ee..5b7e8ee 100644 --- a/solutions/tic-tac-toe.js +++ b/solutions/tic-tac-toe.js @@ -1,288 +1,202 @@ -import React, { useState, useEffect } from 'react'; -import ReactDOM from 'react-dom'; +import React, { useState } from "react" +import { createRoot } from "react-dom/client" const rowStyle = { - display: 'flex' + display: "flex", } const squareStyle = { - 'width':'60px', - 'height':'60px', - 'backgroundColor': '#ddd', - 'margin': '4px', - 'display': 'flex', - 'justifyContent': 'center', - 'alignItems': 'center', - 'fontSize': '20px', - 'color': 'white', - 'cursor': 'pointer', + width: "60px", + height: "60px", + backgroundColor: "#ddd", + margin: "4px", + display: "flex", + justifyContent: "center", + alignItems: "center", + fontSize: "20px", + color: "white", +} + +const disabledSquareStyle = { + ...squareStyle, + cursor: "not-allowed", } const boardStyle = { - 'backgroundColor': '#eee', - 'width': '208px', - 'alignItems': 'center', - 'justifyContent': 'center', - 'display': 'flex', - 'flexDirection': 'column', - 'border': '3px #eee solid' + backgroundColor: "#eee", + width: "208px", + alignItems: "center", + justifyContent: "center", + display: "flex", + flexDirection: "column", + border: "3px #eee solid", } const containerStyle = { - 'display': 'flex', - 'alignItems': 'center', - 'flexDirection': 'column' + display: "flex", + alignItems: "center", + flexDirection: "column", } const instructionsStyle = { - 'marginTop': '5px', - 'marginBottom': '5px', - 'fontWeight': 'bold', - 'fontSize': '16px', + marginTop: "5px", + marginBottom: "5px", + fontWeight: "bold", + fontSize: "16px", } const buttonStyle = { - 'marginTop': '15px', - 'marginBottom': '16px', - 'width': '80px', - 'height': '40px', - 'backgroundColor': '#8acaca', - 'color': 'white', - 'fontSize': '16px', - 'cursor': 'pointer', + marginTop: "15px", + marginBottom: "16px", + width: "80px", + height: "40px", + backgroundColor: "#8acaca", + color: "white", + fontSize: "16px", } -const Square = ({playerXSquares, playerOSquares, handleClick, value, disabled }) => { - return ( -
    { - if (!disabled) { - handleClick(value) - } - }} - > - - {playerXSquares.indexOf(value) > -1 ? 'X' : playerOSquares.indexOf(value) > -1 ? 'O' : ''} - -
    - ); -}; +const Square = ({ value, onClick, winner }) => { + return ( + + ) +} const Board = () => { - const [currentPlayer, setCurrentPlayer] = useState('player X'); - const [winner, setWinner] = useState('None') - const [playerXSquares, setPlayerXSquares] = useState([]); - const [playerOSquares, setPlayerOSquares] = useState([]); - - const handleClick = value => { - if (currentPlayer === 'player X') { - setPlayerXSquares([...playerXSquares, value]); - setCurrentPlayer('player O'); - } else { - setPlayerOSquares([...playerOSquares, value]); - setCurrentPlayer('player X'); - } - } - - const determineWinner = (playerXSquares, playerOSquares) => { - function checkRows() { - if ( - (playerXSquares.includes(0) && playerXSquares.includes(1) && playerXSquares.includes(2)) || - (playerXSquares.includes(3) && playerXSquares.includes(4) && playerXSquares.includes(5)) || - (playerXSquares.includes(6) && playerXSquares.includes(7) && playerXSquares.includes(8)) - ) { - return setWinner('Player X'); - } - - if ( - (playerOSquares.includes(0) && playerOSquares.includes(1) && playerOSquares.includes(2)) || - (playerOSquares.includes(3) && playerOSquares.includes(4) && playerOSquares.includes(5)) || - (playerOSquares.includes(6) && playerOSquares.includes(7) && playerOSquares.includes(8)) - ) { - return setWinner('Player O'); - } + const [player, setPlayer] = useState("X") + const [winner, setWinner] = useState("None") + const [board, setBoard] = useState(() => Array(9).fill(null)) + + const matches = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ] + + const checkWinner = newBoard => { + for (let i = 0; i < matches.length; i++) { + const [a, b, c] = matches[i] + if ( + newBoard[a] === player && + newBoard[b] === player && + newBoard[c] === player + ) { + return true + } + } + return false } - function checkColumns() { - if ( - (playerXSquares.includes(0) && playerXSquares.includes(3) && playerXSquares.includes(6)) || - (playerXSquares.includes(1) && playerXSquares.includes(4) && playerXSquares.includes(7)) || - (playerXSquares.includes(2) && playerXSquares.includes(5) && playerXSquares.includes(8)) - ) { - return setWinner('Player X'); - } - - if ( - (playerOSquares.includes(0) && playerOSquares.includes(3) && playerOSquares.includes(6)) || - (playerOSquares.includes(1) && playerOSquares.includes(4) && playerOSquares.includes(7)) || - (playerOSquares.includes(2) && playerOSquares.includes(5) && playerOSquares.includes(8)) - ) { - return setWinner('Player O'); - } - } + const onClick = index => { + const newBoard = [...board] + newBoard[index] = player + setBoard(newBoard) - function checkDiagonals() { - if (playerXSquares.includes(4)) { - if ((playerXSquares.includes(0) && playerXSquares.includes(8)) || (playerXSquares.includes(2) && playerXSquares.includes(6))) { - return setWinner('Player X'); + if (checkWinner(newBoard)) { + setWinner(player) + } else { + setPlayer(player === "X" ? "O" : "X") } - } - if (playerOSquares.includes(4)) { - if ((playerOSquares.includes(0) && playerOSquares.includes(8)) || (playerOSquares.includes(2) && playerOSquares.includes(6))) { - return setWinner('Player O'); - } - } } - if (winner === 'None') { - checkRows(); - checkColumns(); - checkDiagonals(); + const onReset = () => { + setBoard(Array(9).fill(null)) + setPlayer("X") + setWinner("None") } - } - - - const reset = () => { - setCurrentPlayer('Player X'); - setPlayerXSquares([]); - setPlayerOSquares([]); - setWinner('None'); - } - useEffect(() => { - determineWinner(playerXSquares, playerOSquares); - }, [playerXSquares, playerOSquares]); - - return ( -
    -
    - Next player: {currentPlayer === 'Player X' ? 'Player O' : 'Player X'} -
    -
    - Winner: {winner} -
    - -
    -
    - - - -
    -
    - - - -
    -
    - - - + return ( +
    +
    + Next player: {player === "X" ? "O" : "X"} +
    +
    + Winner: {winner !== "None" && winner} +
    + +
    +
    + onClick(0)} + winner={winner} + /> + onClick(1)} + winner={winner} + /> + onClick(2)} + winner={winner} + /> +
    +
    + onClick(3)} + winner={winner} + /> + onClick(4)} + winner={winner} + /> + onClick(5)} + winner={winner} + /> +
    +
    + onClick(6)} + winner={winner} + /> + onClick(7)} + winner={winner} + /> + onClick(8)} + winner={winner} + /> +
    +
    -
    -
    - ); -}; + ) +} const Game = () => { - return ( -
    -
    - -
    -
    - ); -}; + return ( +
    +
    + +
    +
    + ) +} -ReactDOM.render( - , - document.getElementById('root') -); \ No newline at end of file +const root = createRoot(document.getElementById("root")) +root.render() From a6d33d57a9388bd92e398ed2b6bc8839db3bd31c Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Sat, 20 Jan 2024 01:20:32 -0500 Subject: [PATCH 09/15] chore: change file extensions from js/ts to jsx/tsx --- solutions/{button-toggle.js => button-toggle.jsx} | 0 solutions/{context-api.js => context-api.jsx} | 0 solutions/{list.js => list.jsx} | 0 solutions/{phone-book.js => phone-book.jsx} | 0 ...t-native-simple-counter.js => react-native-simple-counter.jsx} | 0 solutions/{simple-counter.js => simple-counter.jsx} | 0 solutions/{tic-tac-toe.js => tic-tac-toe.jsx} | 0 .../{typescript-button-toggle.ts => typescript-button-toggle.tsx} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename solutions/{button-toggle.js => button-toggle.jsx} (100%) rename solutions/{context-api.js => context-api.jsx} (100%) rename solutions/{list.js => list.jsx} (100%) rename solutions/{phone-book.js => phone-book.jsx} (100%) rename solutions/{react-native-simple-counter.js => react-native-simple-counter.jsx} (100%) rename solutions/{simple-counter.js => simple-counter.jsx} (100%) rename solutions/{tic-tac-toe.js => tic-tac-toe.jsx} (100%) rename solutions/{typescript-button-toggle.ts => typescript-button-toggle.tsx} (100%) diff --git a/solutions/button-toggle.js b/solutions/button-toggle.jsx similarity index 100% rename from solutions/button-toggle.js rename to solutions/button-toggle.jsx diff --git a/solutions/context-api.js b/solutions/context-api.jsx similarity index 100% rename from solutions/context-api.js rename to solutions/context-api.jsx diff --git a/solutions/list.js b/solutions/list.jsx similarity index 100% rename from solutions/list.js rename to solutions/list.jsx diff --git a/solutions/phone-book.js b/solutions/phone-book.jsx similarity index 100% rename from solutions/phone-book.js rename to solutions/phone-book.jsx diff --git a/solutions/react-native-simple-counter.js b/solutions/react-native-simple-counter.jsx similarity index 100% rename from solutions/react-native-simple-counter.js rename to solutions/react-native-simple-counter.jsx diff --git a/solutions/simple-counter.js b/solutions/simple-counter.jsx similarity index 100% rename from solutions/simple-counter.js rename to solutions/simple-counter.jsx diff --git a/solutions/tic-tac-toe.js b/solutions/tic-tac-toe.jsx similarity index 100% rename from solutions/tic-tac-toe.js rename to solutions/tic-tac-toe.jsx diff --git a/solutions/typescript-button-toggle.ts b/solutions/typescript-button-toggle.tsx similarity index 100% rename from solutions/typescript-button-toggle.ts rename to solutions/typescript-button-toggle.tsx From b8c5bd4ebf374b017b7ae62672c75e245358fdbe Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Sat, 20 Jan 2024 01:29:59 -0500 Subject: [PATCH 10/15] feat: add color-dropdown.jsx solution --- solutions/color-dropdown.jsx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 solutions/color-dropdown.jsx diff --git a/solutions/color-dropdown.jsx b/solutions/color-dropdown.jsx new file mode 100644 index 0000000..17e5e89 --- /dev/null +++ b/solutions/color-dropdown.jsx @@ -0,0 +1,21 @@ +import React, { useState } from "react" +import { createRoot } from "react-dom/client" + +function ColorSelector() { + const colors = { red: "Red", blue: "Blue", green: "Green" } + const [color, setColor] = useState(colors.red) + const onChange = e => setColor(e.target.value) + return ( + <> + + {color &&

    {`You have selected: ${color}`}

    } + + ) +} + +const root = createRoot(document.getElementById("root")) +root.render() From 04d8498b4dc620c5d9522fef7252220defefeeb7 Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Sat, 20 Jan 2024 01:30:26 -0500 Subject: [PATCH 11/15] feat: add live-paragraph.jsx solution --- solutions/live-paragraph.jsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 solutions/live-paragraph.jsx diff --git a/solutions/live-paragraph.jsx b/solutions/live-paragraph.jsx new file mode 100644 index 0000000..e5b9ea9 --- /dev/null +++ b/solutions/live-paragraph.jsx @@ -0,0 +1,19 @@ +import React, { useState } from "react" +import { createRoot } from "react-dom/client" + +function LiveText() { + const [text, setText] = useState("") + + const onChange = ({ target: { value } }) => { + setText(value) + } + return ( + <> + +

    {text}

    + + ) +} + +const root = createRoot(document.getElementById("root")) +root.render() From 036d970d758af3dba98ddf1211504c0a54560a2b Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Sat, 20 Jan 2024 01:31:31 -0500 Subject: [PATCH 12/15] feat: add quiz-builder.jsx solution --- solutions/quiz-builder.jsx | 130 +++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 solutions/quiz-builder.jsx diff --git a/solutions/quiz-builder.jsx b/solutions/quiz-builder.jsx new file mode 100644 index 0000000..6d96c0f --- /dev/null +++ b/solutions/quiz-builder.jsx @@ -0,0 +1,130 @@ +import React, { useState, useMemo } from "react" +import { createRoot } from "react-dom/client" + +const style = { + container: { + padding: "20px", + border: "1px solid #E0E0E0", + borderRadius: "15px", + width: "max-content", + marginBottom: "40px", + }, + question: { + fontWeight: "bold", + marginBottom: "10px", + }, + options: { + marginBottom: "5px", + }, + button: { + marginTop: "10px", + padding: "10px 15px", + border: "none", + backgroundColor: "#007BFF", + color: "#FFF", + fontSize: "14px", + borderRadius: "5px", + cursor: "pointer", + }, + feedback: { + marginTop: "10px", + fontSize: "14px", + }, +} + +const QuizOption = ({ option, index, answer, setAnswer }) => { + const onChange = ({ target: { value } }) => setAnswer(value) + return ( + <> + + + + ) +} + +function QuizApp() { + // do not modify the questions or answers below + const questions = useMemo( + () => [ + { + question: "What is the capital of France?", + options: ["London", "Paris", "Berlin", "Madrid"], + correct: "Paris", + }, + { + question: "What is the capital of Germany?", + options: ["Berlin", "Munich", "Frankfurt", "Hamburg"], + correct: "Berlin", + }, + ], + [] + ) + + const questionsTotal = useMemo(() => questions.length, [questions]) + + const [questionsIndex, setQuestionsIndex] = useState(0) + const [score, setScore] = useState(0) + const [feedback, setFeedback] = useState(null) + const [answer, setAnswer] = useState(null) + const [completedQuiz, setCompletedQuiz] = useState(false) + + const submit = () => { + if (answer === questions[questionsIndex].correct) { + setScore(score + 1) + setFeedback("Correct!") + } else { + setFeedback("Incorrect!") + } + + if (questionsIndex === questionsTotal - 1) { + setCompletedQuiz(true) + } else { + setQuestionsIndex(questionsIndex + 1) + setAnswer(null) + } + } + + return ( +
    +
    + {`${questions[questionsIndex].question}`} +
    +
    + {questions[questionsIndex].options.map((option, index) => ( + + ))} +
    + +
    + {questionsIndex !== 0 && !completedQuiz && `${feedback}`} +
    +
    + {completedQuiz && + `Quiz complete! You scored ${score} out of ${questions.length}!`} +
    +
    + ) +} + +const root = createRoot(document.getElementById("root")) +root.render() From 33df2b613d44ff9c5372a5c194c5d7dbd8009c5a Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Sat, 20 Jan 2024 01:32:40 -0500 Subject: [PATCH 13/15] feat: add letter-tiles.jsx solution --- solutions/letter-tiles.jsx | 83 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 solutions/letter-tiles.jsx diff --git a/solutions/letter-tiles.jsx b/solutions/letter-tiles.jsx new file mode 100644 index 0000000..b8d6e81 --- /dev/null +++ b/solutions/letter-tiles.jsx @@ -0,0 +1,83 @@ +import React, { useState, useCallback, useMemo } from "react" +import { createRoot } from "react-dom/client" + +const style = { + letterContainer: { + overflow: "auto", + marginBottom: "10px", + display: "flex", + flexWrap: "wrap", + justifyContent: "center", + alignItems: "center", + }, + letter: { + float: "left", + padding: "10px 10px", + background: "#c9e4ed", + borderRadius: "5px", + marginRight: "5px", + marginTop: "5px", + cursor: "pointer", + }, + outputString: { + marginTop: "20px", + textAlign: "center", + }, +} + +const Tile = ({ letter, outputArray, setOutputArray, tally, setTally }) => { + const onClick = useCallback(() => { + if (!tally[letter]) { + setTally({ ...tally, [`${letter}`]: 1 }) + setOutputArray([...outputArray, letter]) + } else if (tally[`${letter}`] && tally[`${letter}`] === 2) { + setTally({ ...tally, [`${letter}`]: 0 }) + const slicedArray = outputArray.slice(0, outputArray.length - 2) + setOutputArray([...slicedArray, "_"]) + } else { + setTally({ ...tally, [`${letter}`]: tally[`${letter}`] + 1 }) + setOutputArray([...outputArray, letter]) + } + }, [letter, outputArray, setOutputArray, tally, setTally]) + + return ( + + ) +} + +const Application = () => { + const [outputArray, setOutputArray] = useState([]) + const [tally, setTally] = useState({}) + const alphabetArray = useMemo(() => { + let arr = [] + for (let i = 65; i <= 90; i++) { + const char = String.fromCharCode(i) + arr.push(char) + } + return arr + }, []) + return ( +
    + +
    + {outputArray.join("")} +
    +
    + ) +} + +const root = createRoot(document.getElementById("root")) +root.render() From 0e687af776dd899ef69275cc0f42a60a95a79fcb Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Sat, 20 Jan 2024 04:39:35 -0500 Subject: [PATCH 14/15] feat: add weather-dashboard.jsx solution --- solutions/weather-dashboard.jsx | 121 ++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 solutions/weather-dashboard.jsx diff --git a/solutions/weather-dashboard.jsx b/solutions/weather-dashboard.jsx new file mode 100644 index 0000000..677f148 --- /dev/null +++ b/solutions/weather-dashboard.jsx @@ -0,0 +1,121 @@ +import React, { useState } from "react" +import { createRoot } from "react-dom/client" + +const style = { + marginTop: { + marginTop: "10px", + }, + marginRight: { + marginRight: "10px", + }, +} + +const WeatherDashboard = () => { + // instead of requesting data from an API, use this mock data + const mockWeatherData = { + "New York": { + temperature: "22°C", + humidity: "56%", + windSpeed: "15 km/h", + }, + "Los Angeles": { + temperature: "27°C", + humidity: "45%", + windSpeed: "10 km/h", + }, + London: { + temperature: "15°C", + humidity: "70%", + windSpeed: "20 km/h", + }, + } + + const defaultWeather = { + temperature: "", + humidity: "", + windSpeed: "", + } + + const [city, setCity] = useState("") + const [cache, setCache] = useState({}) + const [notFound, setNotFound] = useState(false) + const [previousSearches, setPreviousSearches] = useState([]) + const [weather, setWeather] = useState(defaultWeather) + + const mockFetchWeatherData = city => { + return new Promise((resolve, reject) => { + setTimeout(() => { + if (mockWeatherData[city]) { + resolve(mockWeatherData[city]) + } else { + reject(new Error("City not found.")) + setNotFound(true) + setWeather(defaultWeather) + } + }, 500) + }) + } + + const search = async city => { + setNotFound(false) + + if (!city || city === "") { + setWeather(defaultWeather) + setNotFound(true) + return + } + + if (cache[city]) { + setWeather(cache[city]) + return + } + + try { + const data = await mockFetchWeatherData(city) + setCache({ ...cache, [city]: data }) + setWeather(data) + setPreviousSearches([...previousSearches, city]) + } catch { + throw new Error("Could not fetch weather data.") + } + } + + return ( +
    + setCity(e.target.value)} + /> + +
    +
    Temperature: {weather.temperature}
    +
    Humidity: {weather.humidity}
    +
    Wind Speed: {weather.windSpeed}
    + {notFound &&
    City not found.
    } +
    +
    + {previousSearches.length > 0 && + previousSearches.map((previousSearch, index) => ( + + ))} +
    +
    + ) +} + +const root = createRoot(document.getElementById("root")) +root.render() From 297afef2abd1881838cfc43e46c7904ca94696df Mon Sep 17 00:00:00 2001 From: Kayla Kremer Date: Sat, 20 Jan 2024 04:46:47 -0500 Subject: [PATCH 15/15] chore: update README --- README.md | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c5c5bc2..53930c1 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,43 @@ # Coderbyte React Challenges -- This repo contains my solutions for the [React Interview Kit](https://coderbyte.com/interview-kit/react) challenges. +- This repo contains my solutions for the [React & React Native challenges](https://coderbyte.com/challenges). - All solutions received a 10/10 score. - Feel free to use any of my solutions for yourself! -### Update 1/8/23 +## Updates -I added the missing solutions to the newer challenges Coderbyte made for React/React Native since I first created this repo. As of now, all challenges are solved. Since this repo seems to be popular, I will try to periodically check and update it with more solutions if Coderbyte decides to add more React challenges. +#### 1/8/23 -### Update 6/13/23 +Added missing solutions for the newer challenges Coderbyte released. -Refactored a few solutions! +#### 6/13/23 -### Update 9/21/23 -Slight editing of README. +Refactored a few solutions. + +#### 9/21/23 + +Slight editing of `README`. + +#### 1/20/24 + +- Refactored all previous solutions to use React 18 and use .jsx/.tsx file extensions +- Redid solution for `tictactoe.jsx` +- Added new solutions for the following challenges: + - `color-dropdown.jsx` + - `letter-tiles.jsx` + - `live-paragraph.jsx` + - `quiz-builder.jsx` + - `weather-dashboard.jsx` +- Updated `README`` + +## FAQ + +**Can you include the challenge's question in your solution?** + +Unfortunately, no since I don't want to violate Coderbyte's [Terms of Use](https://coderbyte.com/terms). My solutions are only meant as a resource to check against your own or just to study from in general! You'll need to pay for Coderbyte to see the questions to their code challenges. + +**Do you accept submissions for alternative solutions?** + +No, not at this time. I originally intended to use this repo as a way to keep a copy of my solutions I wrote when I ended my Coderbyte subscription. However, since this repo has grown in popularity, if there is more interest to open this repo up and accept alternative solution submissions, I may consider this!