ItIron2023
react
我們昨天做了一個簡單的pagination題目,大致上了解一般實務是如何處理這類的情況,包含loading & hasMore的小細節,只要你能掌握基本的useState & useEffect應該就難不倒你!今天我們來看一個變化題吧,這並不是實務上真正會出現的情境,但卻是我之前在面試一間多媒體美商時遇到的面試問題,後來也發現其他公司也有出過類似的題目,就把這個題目收藏起來了,馬上來瞧一瞧吧!
請觀察這個codesandbox以及下方的starter code。
import "./styles.css";
function App() {
const fruitEmojis = ["🍎", "🍊", "🍇", "🍓", "🍒"];
return (
<div
style={{ width: "100vw", height: "100vh", position: "relative" }}
>
<h1>Click to add an fruit emoji 🍎🍊🍇</h1>
// Display your emojis
</div>
);
}
export default App;
.emoji {
height: 24px;
width: 24px;
display: grid;
place-items: center;
user-select: none;
}
今天你要做的事情相當單純,你需要在使用者點擊畫面中的任何地方時建立一個Emoji,如下圖所示
請注意以下的事項並完成題目的要求
1. 每次點擊時應從fruitEmojis陣列中隨機選一個emoji產生
2. 請勿移除styles.css中.emoji的任何樣式(你可以新增),並請勿移除起始js檔案內的樣式
3. 產生的emoji元素需用指定的格式<span img='role' className="emoji">Emoji放這</span>
4. 你可以使用event.clientX & event.clientY來取得使用者點擊的位置
這是個有趣的題目,基本上我是忠實呈現當時被問的情境,唯獨我給予你們更多的提示以便我加快速度,首先要做到這樣的效果我們勢必需要一個state管理所有產生的emoji(其實這句話不一定正確,你大可以直接在畫面加上emoji而不用任何的state管理,但這樣後續要操作這些產生的emoji難度就提高了不少)。
你大概可以猜到我們最終需要一個陣列並配合map方法渲染出所有的emojis,最好還另外做一個獨立的Emoji組件,那麼我們就必須釐清這個emoji有哪些元素是必備的
1. 使用者點擊的x座標
2. 使用者點擊的y座標
3. 一個獨特的id
4. 是哪個水果emoji
有了這些之後你的Emoji組件就呼之欲出了,有了x & y座標,接著我們只要配合position: 'absolute'就可以輕易讓emoji出現在我們的點擊的位置,你可以先完成以下的部分,以下是其中一種寫法。
import React, { useState } from "react";
const Emoji = ({ x, y, emoji }) => (
<span
className="emoji"
role="img"
style={{
position: "absolute",
left: `${x}px`,
top: `${y}px`
}}
>
{emoji}
</span>
);
function App() {
const [emojis, setEmojis] = useState([]);
const fruitEmojis = ["🍎", "🍊", "🍇", "🍓", "🍒"];
return (
<div
style={{ width: "100vw", height: "100vh", position: "relative" }}
>
<h1>Click to add an fruit emoji 🍎🍊🍇</h1>
{emojis.map((emojiObj) => (
<Emoji
key={emojiObj.id}
x={emojiObj.x}
y={emojiObj.y}
emoji={emojiObj.emoji}
/>
))}
</div>
);
}
export default App;
到這邊遊戲基本上就結束了,你只要再加上一個點擊的handler就可以順利完成題目,由於你需要在畫面上任何一個地方點擊都能產生emoji,你的onClick自然就需要掛載到最外層的div元素上,以下是一個基本的handleClick函數範例,獨特id的部分就容我偷懶一下用timestamp完成
function App() {
const [emojis, setEmojis] = useState([]);
const fruitEmojis = ["🍎", "🍊", "🍇", "🍓", "🍒"];
const handleClick = (e) => {
const x = e.clientX;
const y = e.clientY;
const randomEmoji =
fruitEmojis[Math.floor(Math.random() * fruitEmojis.length)];
// 每次點擊時更新emoji陣列
setEmojis((prevEmojis) => [
...prevEmojis,
{ id: Date.now(), x, y, emoji: randomEmoji }
]);
};
return (
<div
style={{ width: "100vw", height: "100vh", position: "relative" }}
onClick={handleClick} // 加入click handler
>
<h1>Click to add an fruit emoji 🍎🍊🍇</h1>
{emojis.map((emojiObj) => (
<Emoji
key={emojiObj.id}
x={emojiObj.x}
y={emojiObj.y}
emoji={emojiObj.emoji}
/>
))}
</div>
);
}
export default App;
實際去操作後你會發現確實有在點擊的地方順利產生emoji,不過位置稍稍有些偏差,類似下圖的效果。
這主要是因為我們是根據使用者點擊的位置來抓x & y座標,抓到點後會從左上角放置該emoji,這麼一來最終呈現的位置就會受到emoji本身的長寬影響導致看起來有些落差,因此你需要對展示的位置做一些微調,例如下方就會是根據元素本身的長寬做一些調整,這部分就完全根據UI的需求,若你需要讓產生的emoji完全在正中央,那麼你就得多下一點點工夫
const Emoji = ({ x, y, emoji }) => (
<span
className="emoji"
role="img"
style={{
position: "absolute",
left: `${x - 24}px`, // 根據元素的寬度微調
top: `${y - 24}px` // 根據元素的長度微調
}}
>
{emoji}
</span>
);
今天我們看了另一個有趣的題目,至少在畫面上看起來是更多采多姿一些,題目主要考驗點擊事件的處理以及稍稍有些複雜的state,在渲染陣列時也有map & key這些小地方需要注意,算是個不錯的起始問題,明天我們會根據這個問題繼續做一些延伸,完整地去模擬我當時面試的狀況,敬請期待!
本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!