자동완성
import { useState, useEffect } from 'react';
import styled from 'styled-components';
const deselectedOptions = [
'rustic',
'antique',
'vinyl',
'vintage',
'refurbished',
'신품',
'빈티지',
'중고A급',
'중고B급',
'골동품'
];
/* TODO : 아래 CSS를 자유롭게 수정하세요. */
const boxShadow = '0 4px 6px rgb(32 33 36 / 28%)';
const activeBorderRadius = '1rem 1rem 0 0';
const inactiveBorderRadius = '1rem 1rem 1rem 1rem';
export const InputContainer = styled.div`
margin-top: 8rem;
background-color: #ffffff;
display: flex;
flex-direction: row;
padding: 1rem;
border: 1px solid rgb(223, 225, 229);
border-radius: ${inactiveBorderRadius};
z-index: 3;
box-shadow: 0;
&:focus-within {
box-shadow: ${boxShadow};
}
> input {
flex: 1 0 0;
background-color: transparent;
border: none;
margin: 0;
padding: 0;
outline: none;
font-size: 16px;
}
> div.delete-button {
cursor: pointer;
}
`;
export const DropDownContainer = styled.ul`
background-color: #ffffff;
display: block;
margin-left: auto;
margin-right: auto;
list-style-type: none;
margin-block-start: 0;
margin-block-end: 0;
margin-inline-start: 0px;
margin-inline-end: 0px;
padding-inline-start: 0px;
margin-top: -1px;
padding: 0.5rem 0;
border: 1px solid rgb(223, 225, 229);
border-radius: 0 0 1rem 1rem;
box-shadow: ${boxShadow};
z-index: 3;
> li {
padding: 0 1rem;
}
`;
export const Autocomplete = () => {
/**
* Autocomplete 컴포넌트는 아래 3가지 state가 존재합니다. 필요에 따라서 state를 더 만들 수도 있습니다.
* - hasText state는 input값의 유무를 확인할 수 있습니다.
* - inputValue state는 input값의 상태를 확인할 수 있습니다.
* - options state는 input값을 포함하는 autocomplete 추천 항목 리스트를 확인할 수 있습니다.
*/
const [hasText, setHasText] = useState(false);
const [inputValue, setInputValue] = useState('');
const [options, setOptions] = useState(deselectedOptions);
// useEffect를 아래와 같이 활용할 수도 있습니다. //지웠을때?
useEffect(() => {
if (inputValue === '') {
setHasText(false);
}
}, [inputValue]);
// TODO : input과 dropdown 상태 관리를 위한 handler가 있어야 합니다.
const handleInputChange = (event) => {
/**
* handleInputChange 함수는
* - input값 변경 시 발생되는 change 이벤트 핸들러입니다.
* - input값과 상태를 연결시킬 수 있게 controlled component로 만들 수 있고
* - autocomplete 추천 항목이 dropdown으로 시시각각 변화되어 보여질 수 있도록 상태를 변경합니다.
*
* handleInputChange 함수를 완성하여 아래 3가지 기능을 구현합니다.
*
* onChange 이벤트 발생 시
* 1. input값 상태인 inputValue가 적절하게 변경되어야 합니다.
* 2. input값 유무 상태인 hasText가 적절하게 변경되어야 합니다.
* 3. autocomplete 추천 항목인 options의 상태가 적절하게 변경되어야 합니다.
* Tip : options의 상태에 따라 dropdown으로 보여지는 항목이 달라집니다.
*/
setInputValue(event.target.value);
setHasText(true);
setOptions(deselectedOptions.filter(el=>el.includes(event.target.value.toLowerCase())))
// 예를 들어 문자열 r이 포함된 모든요소 리턴...? 필터?
};
const handleDropDownClick = (clickedOption) => {
/**
* handleDropDownClick 함수는
* - autocomplete 추천 항목을 클릭할 때 발생되는 click 이벤트 핸들러입니다.
* - dropdown에 제시된 항목을 눌렀을 때, input값이 해당 항목의 값으로 변경되는 기능을 수행합니다.
*
* handleInputChange 함수를 완성하여 아래 기능을 구현합니다.
*
* onClick 이벤트 발생 시
* 1. input값 상태인 inputValue가 적절하게 변경되어야 합니다.
* 2. autocomplete 추천 항목인 options의 상태가 적절하게 변경되어야 합니다.
*/
setInputValue(clickedOption)//클릭된 값
};
const handleDeleteButtonClick = () => {
/**
* handleDeleteButtonClick 함수는
* - input의 오른쪽에 있는 X버튼 클릭 시 발생되는 click 이벤트 핸들러입니다.
* - 함수 작성을 완료하여 input값을 한 번에 삭제하는 기능을 구현합니다.
*
* handleDeleteButtonClick 함수를 완성하여 아래 기능을 구현합니다.
*
* onClick 이벤트 발생 시
* 1. input값 상태인 inputValue가 빈 문자열이 되어야 합니다.
*/
setInputValue('')
};
// Advanced Challenge: 상하 화살표 키 입력 시 dropdown 항목을 선택하고, Enter 키 입력 시 input값을 선택된 dropdown 항목의 값으로 변경하는 handleKeyUp 함수를 만들고,
// 적절한 컴포넌트에 onKeyUp 핸들러를 할당합니다. state가 추가로 필요한지 고민하고, 필요 시 state를 추가하여 제작하세요.
return (
<div className='autocomplete-wrapper'>
<InputContainer>
{/* TODO : input 엘리먼트를 작성하고 input값(value)을 state와 연결합니다. handleInputChange 함수와 input값 변경 시 호출될 수 있게 연결합니다. */}
<input
onChange={handleInputChange}
value={inputValue}
type='text'
/>
{/* TODO : 아래 div.delete-button 버튼을 누르면 input 값이 삭제되어 dropdown이 없어지는 handler 함수를 작성합니다. */}
<div onClick={handleDeleteButtonClick} className='delete-button'>×</div>
</InputContainer>
{/* TODO : input 값이 없으면 dropdown이 보이지 않아야 합니다. 조건부 렌더링을 이용해서 구현하세요. */}
{hasText ? <DropDown handleDropDownClick={handleDropDownClick} options={options} handleComboBox={handleInputChange} /> : null}
</div>
);
};
export const DropDown = ({ options, handleComboBox,handleDropDownClick }) => {
return (
<DropDownContainer>
{/* TODO : input 값에 맞는 autocomplete 선택 옵션이 보여지는 역할을 합니다. */}
{options.map((el,index)=>{
return <li onClick={()=>handleDropDownClick(el)} key={index}>{el}</li>
})}
</DropDownContainer>
);
};
헷갈렸던 점 1
const handleInputChange = (event) => {
setInputValue(event.target.value);
setHasText(true);
setOptions(deselectedOptions.filter(el=>el.includes(event.target.value.toLowerCase())))
};
setHasText(여기에 무엇을 적어줘야할지 헷갈렸는데)
같은 컴포넌트안에 useEffect훅이 있었다.
useEffect(() => {
if (inputValue === '') {
setHasText(false);
}
}, [inputValue]);
input의 value가 변경되었을때 true가 되지만
만약 사용자가 input 에 검색어 입력"후"다 지운다면
그때는 false값이 되어야 한다.
useEffect훅을 사용하지 않았다면, 계속 setHasText는 true값일것이다.
헷갈렸던 점 2
setOptions(deselectedOptions.filter(el=>el.includes(event.target.value.toLowerCase())))
이부분도 filter에는 무슨 배열을 걸러야 할까 고민했는데
value가 변할때마다 모든 데이터를 가지고 있는 배열에서 같은 글자를 찾아야하기때문에
deselectedOptions를 쓰는것이 맞다.
헷갈렸던 점 3
<div className='autocomplete-wrapper'> //중략
{hasText ? <DropDown handleDropDownClick={handleDropDownClick} options={options} handleComboBox={handleInputChange} /> : null}
</div>
export const DropDown = ({ options, handleComboBox,handleDropDownClick }) => {
return (
<DropDownContainer>
{/* TODO : input 값에 맞는 autocomplete 선택 옵션이 보여지는 역할을 합니다. */}
{options.map((el,index)=>{
return <li onClick={()=>handleDropDownClick(el)} key={index}>{el}</li>
})}
</DropDownContainer>
);
};
props로 넘겨주는 부분이 여전히 헷갈린다!
부모 => 자식 컴포넌트로 전달해줄때
속성값을 적기만 하면되는데
모드가 너무 장황해서 어버버했다.
자식컴포넌트에서 필요한 함수도 잘 생각하고
특히!! map 사용시 index 인자로 받아서 key 값 까먹지 않기!
Click To Edit
import { useEffect, useState, useRef } from 'react';
import styled from 'styled-components';
export const InputBox = styled.div`
text-align: center;
display: inline-block;
width: 150px;
height: 30px;
border: 1px #bbb dashed;
border-radius: 10px;
margin-left: 1rem;
`;
export const InputEdit = styled.input`
text-align: center;
display: inline-block;
width: 150px;
height: 30px;
`;
export const InputView = styled.div`
text-align: center;
align-items: center;
margin-top: 3rem;
div.view {
margin-top: 3rem;
}
`;
export const MyInput = ({ value, handleValueChange }) => {
const inputEl = useRef(null);
const [isEditMode, setEditMode] = useState(false);
const [newValue, setNewValue] = useState(value);
//보이게될 값 isEditedMode가 true일때 업데이트
useEffect(() => {
if (isEditMode) { // input창을 클릭했을때 true=>포커스메소드실행
inputEl.current.focus();
}
}, [isEditMode]);
useEffect(() => {
setNewValue(value);
}, [value]);
const handleClick = () => {
// TODO : isEditMode 상태를 변경합니다.
setEditMode(true)
};
const handleBlur = () => {
// TODO : Edit가 불가능한 상태로 변경합니다.
handleValueChange(newValue);
//handleValueChange는 ClickToEdit 컴포넌트의 input value를 업데이트한다
setEditMode(false)
};
const handleInputChange = (e) => {
// TODO : 저장된 value를 업데이트합니다.
setNewValue(e.target.value) //사용자가 입력한 새로운 value를 상태변경함수에 넣어준다.
handleValueChange(e.target.value) //새로운 값인 input value를 변경함수에 인자로 전달
};
return (
<InputBox>
{isEditMode ? (
<InputEdit
type='text'
value={newValue}
ref={inputEl}
onBlur={handleBlur}
onChange={handleInputChange}
// TODO : 포커스를 잃으면 Edit가 불가능한 상태로 변경되는 메소드가 실행되어야 합니다.
// TODO : 변경 사항이 감지되면 저장된 value를 업데이트 되는 메소드가 실행되어야 합니다.
/>
) : (
<span onClick={handleClick} // handleClick함수로 editMode 변경
// TODO : 클릭하면 Edit가 가능한 상태로 변경되어야 합니다.
>{newValue}</span>
)}
</InputBox>
);
}
const cache = {
name: '김코딩',
age: 20
};
export const ClickToEdit = () => {
const [name, setName] = useState(cache.name);
const [age, setAge] = useState(cache.age);
return (
<>
<InputView>
<label>이름</label>
<MyInput value={name} handleValueChange={(newValue) => setName(newValue)} />
</InputView>
<InputView>
<label>나이</label>
<MyInput value={age} handleValueChange={(newValue) => setAge(newValue)} />
</InputView>
<InputView>
<div className='view'>이름 {name} 나이 {age}</div>
</InputView>
</>
);
};
헷갈렸던 점 1
일단 전체적으로 코드의 연관성이 헷갈렸다.
input창을 클릭했을 때 edit모드가되며
input외의 부분을 클릭했을 때 edit모드가 종료된다.
여기서 상태는 edit모드와 input의 value
일어나는 이벤트는 onClick과 onChange이다.
onChang가 일어났을 때 handleInputChange함수를 통해서
input값의 상태를 변경해주는 함수인 setNewValue에 value를인자로
부모컴포넌트의 값을 변경하는 함수인 props로 받아온 handleValueChange에도
역시 value를 인자로 넘겨주어 input에 새로운 값이 렌더링되게 해준다.
몰랐던 점
onBlur는 input요소가 포커스를 잃었을때 발생하는 이벤트를 감지하는 메소드이다.
사용자가 특정 input요소를 클릭하고 다른요소를 클릭할때
onBlur이벤트가 발생한다.
'React' 카테고리의 다른 글
[Redux] Redux의 데이터 흐름과 Flux패턴 (3) | 2023.04.24 |
---|---|
[React] 쇼핑몰앱 장바구니기능을 구현해보자 (2) | 2023.04.21 |
리액트로 모달창,토글,탭,태그 구현 (0) | 2023.04.19 |
useEffect훅을 사용해 과제를 풀어보았다. (0) | 2023.04.03 |
[React] Effect Hook (0) | 2023.04.01 |