TIL 40일차

안광의·2021년 8월 11일
0

Today I Learned

목록 보기
40/64
post-thumbnail

시작하며

오늘은 어제에 이어서 styled components를 활용하여 웹페이지 상에서 자주 사용되는 UI를 구현하는 스프린트를 진행하였다. React의 useRef를 활용하고 CSS의 문법과 기능을 복습하면서 새로운 작성 방식을 익힐 수 있었다.

React

Autocomplete

import { useState, useEffect } from 'react';
import styled from 'styled-components';
const deselectedOptions = [
  'rustic',
  'antique',
  'vinyl',
  'vintage',
  'refurbished',
  '신품',
  '빈티지',
  '중고A급',
  '중고B급',
  '골동품'
];
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 = () => {
  const [hasText, setHasText] = useState(false);
  const [inputValue, setInputVaule] = useState('');
  const [options, setOptions] = useState(deselectedOptions);
  useEffect(() => {
    if (inputValue === '') {
      setHasText(false);
    }
  }, [inputValue]);
  const handleInputChange = (event) => {
    const {value} = event.target
    if(value !== "") {
      setHasText(true);
    }
    setInputVaule(value)
    setOptions(deselectedOptions.filter((el) =>  el.toLowerCase().includes(value.toLowerCase())))
  };
  const handleDropDownClick = (clickedOption) => {
    setInputVaule(clickedOption)
    setOptions(deselectedOptions.filter((el) =>  el === clickedOption))
  };
  const handleDeleteButtonClick = () => {
    setInputVaule("")
  };
  return (
    <div className='autocomplete-wrapper'>
      <InputContainer>
      <input type='text' value={inputValue} onChange={(event) => handleInputChange(event)}/>
        {/* TODO : input 엘리먼트를 작성하고 input값(value)을 state와 연결합니다. handleInputChange 함수와 input값 변경 시 호출될 수 있게 연결합니다. */}
        {/* TODO : 아래 div.delete-button 버튼을 누르면 input 값이 삭제되어 dropdown이 없어지는 handler 함수를 작성합니다. */}
        <div className='delete-button' onClick={handleDeleteButtonClick}>&times;</div>
      </InputContainer>
      {hasText? <DropDown options={options} handleComboBox={handleDropDownClick}/> : ""}
    </div>
  );
};
export const DropDown = ({ options, handleComboBox }) => {
  return (
    <DropDownContainer>
      {options.map((el,id) => <li key={id} onClick={() => handleComboBox(el)}>{el}</li>)}
    </DropDownContainer>
  );
};



ClickToEdit

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);
  useEffect(() => {
    if (isEditMode) {
      inputEl.current.focus();
    }
  }, [isEditMode]);
  useEffect(() => {
    setNewValue(value);
  }, [value]);
  const handleClick = () => {
    setEditMode(!isEditMode);
  };
  const handleBlur = () => {
    setEditMode(false);
    handleValueChange(newValue);
  };
  const handleInputChange = (e) => {
    setNewValue(e.target.value);
  };
  return (
    <InputBox >
      {isEditMode ? (
        <InputEdit
          type='text'
          value={newValue}
          ref={inputEl}
          onBlur={handleBlur}
          onChange={handleInputChange}
        />
      ) : (
        <span onClick={handleClick}>{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>
    </>
  );
};



마치며

사실 코드는 어제 작성을 하고 기능도 정상적으로 구현을 했으나, 마지막 테스트 코드가 통과가 안되서 원인을 찾기 위해서 계속 코드를 수정하였다. 결국 스프린트에서 수정된 부분이 테스트 코드에 반영이 되지 않아서 발생한 문제였다고 오늘 코드 스테이츠 측에서 답변을 받았다. 알려준대로 테스트 코드 파일을 수정한 후 바로 모든 테스트를 통과하였다. 작성한 코드에는 이상이 없어서 테스트 코드 문제라고 짐작은 했지만, 이번 스프린트에서 두 가지의 테스트 코드가 발견되었고 그 부분에서 시간을 많이 들여서 코드 스테이츠 측에는 많이 실망을 했다. 코드를 수정했다면 정상적으로 작동되는지 미리 확인하는 것이 당연할 텐데....

profile
개발자로 성장하기

0개의 댓글