[TIL] 230318 React_dropDown, 대용량 처리, progressBar
select 태그 없이 dropDown 구현
function 안 함수들
const navigate = useNavigate();
const menu = ['전략배분 (정적자산배분)', '듀얼모멘텀', 'VAA', 'DAA'];
const [selectedMenu, setSelectedMenu] = useState('전략배분 (정적자산배분)');
const [activeDropdown, setActiveDropdown] = useState(false);
const onClickOption = useCallback(() => {
setActiveDropdown((prev) => !prev);
}, []);
const onClickSelect = useCallback(
(item) => {
setSelectedMenu(item);
switch (item) {
case '듀얼모멘텀':
navigate('/dual_momentum');
break;
case 'VAA':
navigate('/vaa');
break;
case 'DAA':
navigate('/daa');
break;
default:
navigate('/');
}
},
[navigate],
);
element
<DropdownBox
onClick={onClickOption}
activeDropdown={activeDropdown}
>
<Label>{selectedMenu}</Label>
<SelectOptions activeDropdown={activeDropdown}>
{menu.map((item) => (
<Option key={item} onClick={() => onClickSelect(item)}>
{item}
</Option>
))}
</SelectOptions>
</DropdownBox>
css
const DropdownBox = styled.div`
position: relative;
width: 640px;
height: 46px;
background: rgb(14, 14, 14);
border: 1px solid rgb(159, 159, 159);
border-radius: 6px;
padding-top: 12px;
cursor: pointer;
text-align: center;
&::after {
content: url(${arrow});
position: absolute;
top: 14px;
right: 27px;
width: 14px;
transform: ${(props) =>
props.activeDropdown ? `rotate(180deg)` : `unset`};
}
`;
const Label = styled.label`
color: rgb(252, 252, 252);
font-weight: 500;
font-size: 18px;
text-align: center;
`;
const SelectOptions = styled.ul`
display: ${(props) => (props.activeDropdown ? `block` : `none`)};
position: absolute;
top: 50px;
left: 0;
width: 640px;
overflow: hidden;
z-index: 2;
background: rgb(14, 14, 14);
border: 1px solid rgb(62, 62, 62);
border-radius: 6px;
`;
const Option = styled.li`
width: 640px;
padding: 12px 0 12px 0;
text-align: center;
font-weight: 500;
font-size: 18px;
color: rgb(252, 252, 252);
transition: background-color 0.2s ease-in;
&:hover {
background: rgba(236, 97, 38, 0.3);
}
`;
[ERROR] Warning: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`
'onChange' 이벤트 핸들러 없이 form 내부에서 'value' prop를 사용하였습니다. value를 사용한 Element는 읽기 전용으로 렌더링 될 것입니다. 만약 값을 수정하고 싶다면, defaultValue를 사용해야 합니다. 그렇지 않으면, 'onChage' 또는 'readOnly'로 설정해야 합니다.
대용량 처리
※ 모든 자산군 데이터를 스토어에 저장하고 싶다면, 다음과 같은 방법을 사용할 수 있습니다.
1. 자산군 데이터를 미리 로드하여 JSON 파일로 저장합니다.
2. JSON 파일을 읽어와 리덕스 스토어에 저장합니다.
이렇게 하면, 자산군 데이터가 10만개라도 한 번만 불러오면 되므로 성능적으로 더 효율적입니다. 하지만, 자산군 데이터가 변경되는 경우에는 새로운 JSON 파일을 생성하여 업데이트해야 하므로 유지보수에 대한 고려가 필요합니다.
+ React.memo() 사용: 만약 컴포넌트가 불필요하게 재랜더링 되는 경우, 성능이 저하될 수 있습니다. 이런 경우 React.memo()를 사용하여 컴포넌트를 최적화할 수 있습니다. React.memo()는 컴포넌트의 props가 변경될 때만 컴포넌트를 다시 렌더링합니다.
+ 최적화된 CSS 적용: 컴포넌트를 최적화하는 것 외에도, CSS를 최적화하여 성능을 향상시킬 수 있습니다. 예를 들어, 컴포넌트의 크기를 고정하고, 필요한 데이터만 가져와서 보여주는 것
data.json
{
"assets": [
"Agilent Technologies (A-US)",
"동화약품",
"KR모터스",
"경방",
"메리츠화재",
"삼양홀딩스",
"화이트진로",
"유한양행",
"CJ대한통운",
"하이트진로홀딩스",
"두산",
"성창기업지주",
"DL",
"유유제약",
"일동홀딩스",
"삼성전자",
"SK하이닉스",
"우리기술",
"판타지오",
"삼성생명"
]
}
assetSlice.js
import { createSlice } from '@reduxjs/toolkit';
import data from '../data/data.json';
const initialState = {
assets: data.assets,
isLoading: false,
error: null,
};
const assetSlice = createSlice({
name: 'assets',
initialState,
reducers: {},
extraReducers: {},
});
export default assetSlice.reducer;
이렇게 가져온다.
assets: data.assets
api 호출 없이 더미데이터만 보여줘서 reducers와 extraReducers는 필요없었다.
chatGPT 한테 리펙토링시키면 이렇게 알려준다.
- assets 상태 객체를 직접 변경하지 말고, 불변성을 유지하며 업데이트하는 것이 좋습니다. 이를 위해 immer 라이브러리를 사용할 수 있습니다.
- 현재 reducers와 extraReducers 필드가 모두 빈 객체이므로, 이들을 삭제하고 나중에 필요한 경우에 추가하는 것이 좋습니다.
produce 함수는 immer 라이브러리를 사용하여 assets 상태 객체를 불변성을 유지하며 업데이트하는 데 사용됩니다. 이제 assets 상태 객체를 업데이트할 때 다음과 같이 produce 함수를 사용할 수 있습니다.
import produce from 'immer';
...
const assetSlice = createSlice({
name: 'assets',
initialState,
reducers: {
setAssets: (state, action) => {
return produce(state, draftState => {
draftState.assets = action.payload;
});
},
setLoading: (state, action) => {
return produce(state, draftState => {
draftState.isLoading = action.payload;
});
},
setError: (state, action) => {
return produce(state, draftState => {
draftState.error = action.payload;
});
},
},
extraReducers: {
// 필요한 경우 추가
},
});
음 내 일자리 괜찮은 걸까?
ProgressBar
로딩 중 스피너 만들기
https://loading.io/
이 사이트에서 매우 쉽게 스피너를 만들 수 있다!
// 선언문과 함수들
const [isProgress, setIsProgress] = useState(false);
const [width, setWidth] = useState(20);
const [seconds, setSeconds] = useState(10);
useEffect(() => {
let intervalId;
if (width < 210) {
intervalId = setInterval(() => {
setWidth((prevWidth) => prevWidth + (190 / (seconds * 1000)) * 5);
}, 5);
}
return () => {
clearInterval(intervalId);
};
}, [width, seconds]);
// return문
{isProgress && width < 210 ? (
<ProgressBar width={width} />
) : (
<TextWrap>
<BackTestText isClickBacktest={isClickBacktest}>
{isClickBacktest ? '생성중...' : '백테스트'}
</BackTestText>
{isClickBacktest && (
<Spinner>
<img
src={spinner}
alt="로딩중"
width="unset"
height="unset"
/>
</Spinner>
)}
</TextWrap>
)}
// css
const ProgressBar = styled.div`
border-radius: 6px;
background: linear-gradient(to right, rgb(236, 97, 38), rgb(236, 38, 38));
height: 53px;
width: ${(props) => props.width}px;
`;
10초 동안 width가 20px 에서 210px로 증가한다.
최대 width가 됐을 때 ProgressBar를 없어지게 하는 것까지 구현했다.
https://developer-talk.tistory.com/108
[JavaSciprt]Warning : You provided a `value` prop to a form field without an `onChange` handler.
경고 『 Warning: You provided a 'value' prop to a form field without an 'onChange' handler. This will render a read-only field. If the field should be mutable use 'defaultValue'. Otherwise, set either 'onChange' or 'readOnly' 』 해석 'onChange' 이
developer-talk.tistory.com
https://anerim.tistory.com/221
[리액트 React] 리액트 로딩화면(스피너) 추가하기 / api 호출 시 로딩화면(스피너) 넣기
안녕하세요. 디자인도 하고, 개발도 하는 '디발자 뚝딱'입니다. 이번 포스팅에서는 리액트에서 api 호출했을 때 로딩화면/스피너 넣는 방법에 대해 공유하겠습니다. 어렵지 않으니 차례대로 따라
anerim.tistory.com