2 글 보임 - 1 에서 2 까지 (총 2 중에서)
-
글쓴이글
-
2023년 1월 31일 19:20 #66214
아왜나만갖고그래참가자아래는 저기서 선택이 되었을 때 나오는 거
두목님의 조언없이 혼자 해결해보려 했으나 이틀 동안 끙끙앓다가 도저히 안되겠다 싶어 질문남깁니다... 저기 저 레벨 중 하나를 선택하면 그거만 볼드 처리되게 하고 기존에 볼드 처리가 되어있다면 (그러니까 이미 레벨을 선택해서 보고 있다면) 그 볼드처리 된 걸 빼고 가장 최근에 클릭한 레벨을 굵게 표시하고 싶습니다. 지금 이미지에 있는 컴포넌트에서 난이도를 선택하면 형제 컴포넌트(?)에서 상위 카테고리와 난이도에 필터링된 정보만을 뱉어내고 있습니다. 예를 들어 처음에 저 화면을 불러오면 http://localhost:3000/rank/에서 저 컴포넌트만 뜨게 되고 4K의 Lv 18을 선택하면 사전에 설정된 Route와 useNavigate를 통해 http://localhost:3000/rank/4k/18로 연결되고 있습니다. (맨 마지막 뒷 부분은 파라미터 문법으로 처리 중) (편의상 처음에 뜨는 컴포넌트를 A라고 하고 4k/18같은 거 눌렀을 때 나오는 컴포넌트를 B라고 할게여) 그러면 저 B 컴포넌트에 클릭으로 접근하거나, 혹은 URL을 타이핑해서 직접 들어왔을 경우를 대비해 B 컴포넌트가 마운트 되었을 때 현재 무슨 키(4k)와 난이도(18)인지를 state로 저장까지는 해놨습니다마는.. 지금 4k 18에 들어온 걸 어떻게 A컴포넌트로 보내 볼드처리 해주는 클래스 지정을 해주는 건 어떻게 하겠는데 다른 걸 클릭했을 때에는 1. 기존에 붙었던 볼드 처리 클래스를 뗀다. 2. 클릭한 요소에다가 클래스를 부착한다. 이렇게 해야 할 거 같습니다. 근데 대가리에 과부하가 와서 모 데키나이데스..... ----- A 컴포넌트
import { useEffect, useState } from "react"; import { useSelector } from "react-redux"; import { useDispatch } from "react-redux"; import { useNavigate } from "react-router-dom"; import { setTitleView, setKeyAndDifficulty, setDescending, setClass } from "./../store.js"
function RankOrderSelector(){
let state = useSelector((state) => state) let dispatch = useDispatch() let navigate = useNavigate()
function goToPage(key, difficulty){ navigate(`/rank/${key}/${difficulty}`) dispatch(setKeyAndDifficulty({key: key, difficulty: difficulty})) /* 이제 해야 하는 거 4k 18을 셀렉했다 가정 그러면 4k에서 difficulty가 18인 위치의 index에 selected index를 true로 변환 그러면 그에 해당하는 위치를 참조하는 클래스에 bold 붙어기 그리고 다른 거 클릭하면 다시 selected를 원래대로 돌려서 초기화를 시키고 다른 거 클릭한 곳에다가 bold를 붙이기. */ } return ( <div className="gradebox-wrapper-upper"> <div className="gradebox-wrapper-inner"> {/* 4~8키의 서열 난이도 표시하는 부분. */} { state.selectIndex.map((SinglekeyAndDifficulty, index)=>{ return ( <div className="gradebox" key={index}> <h1 className="key-name theme-pp">{SinglekeyAndDifficulty.key.toUpperCase()}</h1> { SinglekeyAndDifficulty.difficulty.map((singleDifficulty, index)=>{ return <span key={index} className={`link ${SinglekeyAndDifficulty.class[index]}`} onClick={()=>{ goToPage(SinglekeyAndDifficulty.key, singleDifficulty) }}>Lv. {singleDifficulty}</span> }) } </div> ) }) } </div> <div className="option"> <label className="switch"> <input type="checkbox" onClick={(e)=>{ dispatch(setTitleView(e.target.checked)) }} defaultChecked></input> <span className="slider round"></span> </label> <span className="bold">곡 제목 표시</span> <label className="switch margin-left-1"> <input type="checkbox" onClick={(e)=>{ dispatch(setDescending(e.target.checked)) }} defaultChecked></input> <span className="slider round"></span> </label> <span className="bold"> 내림차순 </span> </div> </div> ) }
export default RankOrderSelector
---------------------------------------------------------- B 컴포넌트
import { useSelector, useDispatch } from "react-redux"; import axios from 'axios' import { useEffect, useState } from 'react' import { useParams } from "react-router-dom"; import { setKeyAndDifficulty } from './../store.js' // import Skeleton from 'react-loading-skeleton' // import 'react-loading-skeleton/dist/skeleton.css'
function RankOrderList(props){ // 컴포넌트 로드 시 선택된 키, 난이도에 해당하는 자료들을 서버에서 가져와 list 변수에 할당. let state = useSelector( (state) => state ) let dispatch = useDispatch() let {selectedDifficulty} = useParams() let [list, setList] = useState([]) var levelIndex = [[9,8],[7,6],[5,4],[3,2],[1,0]] // 이렇게 var을 써도 되는지 모르겠다 뭔가 var 쓰면 십색기가 되는 느낌
/** RankOrderSelector에서 내림차순 On/Off시 참조할 Index를 변경함. */ if (state.rankUserSelected.isDescending === true){ var levelIndex = [[9,8],[7,6],[5,4],[3,2],[1,0]]; } else { var levelIndex = [[1,0],[3,2],[5,4],[7,6],[9,8]]; }
// 컴포넌트가 처음 마운트되거나, store.js의 rankUserSelected(키, 난이도)가 업데이트 될 때 실행되는 함수. useEffect(()=>{ /** Array 내용 중 선택한 difficulty 값에 해당하는 것만 반환해주는 함수 */ function levelFilter(arr, difficulty){ return arr.filter(el => el.level === difficulty) } /** 유저가 선택한 키 모드에 따라 AJAX 요청을 하기 위해 사용하는 함수 */ function getUrl(key){ return `http://ec2-3-34-144-236.ap-northeast-2.compute.amazonaws.com:54856/rank/list/${key}` }
/** 유저가 RankOrderSelector에서 선택한 키 모드와 세부 난이도를 받아 서열 난이도 내림차순으로 걸러줌.*/ switch(props.selectedKey) { case '4K': axios.get(getUrl('FOUR')) .then((data)=>{ let SelectedKeyFullSongList = data.data.data let filtered = levelFilter(SelectedKeyFullSongList, parseInt(selectedDifficulty)).sort(el => el.rank) setList(filtered) }); break; case '5K': axios.get(getUrl('FIVE')) .then((data)=>{ let SelectedKeyFullSongList = data.data.data let filtered = levelFilter(SelectedKeyFullSongList, parseInt(selectedDifficulty)).sort(el => el.rank) setList(filtered) }) break; case '6K': axios.get(getUrl('SIX')) .then((data)=>{ let SelectedKeyFullSongList = data.data.data let filtered = levelFilter(SelectedKeyFullSongList, parseInt(selectedDifficulty)).sort(el => el.rank) setList(filtered) }) break; case '8K': axios.get(getUrl('EIGHT')) .then((data)=>{ let SelectedKeyFullSongList = data.data.data let filtered = levelFilter(SelectedKeyFullSongList, parseInt(selectedDifficulty)).sort(el => el.rank) setList(filtered) }) break; }
/** RankOrderSelector 컴포넌트에서 현재 선택한 키와 난이도가 선택될 때 store.js에 저장된 유저 선택값을 바꿔줌. */ dispatch(setKeyAndDifficulty({key: props.selectedKey, difficulty: parseInt(selectedDifficulty)})) }, [state.rankUserSelected.selectedDifficulty, state.rankUserSelected.selectedKey])
/** 서열 난이도의 단계를 받아 하 ~ 최상으로 반환해주는 함수 */ function detailDifficultyFilter(detailDifficulty){ switch(detailDifficulty){ case 0: case 1: return '하'; case 2: case 3: return '중하'; case 4: case 5: return '중'; case 6: case 7: return '중상'; case 8: case 9: return '최상'; } }
return ( <div className="flexbox" style={{'height':'10000px'}}> <h1 className="theme-pp header-margin header">{props.selectedKey}</h1> {/* Songs 클래스 네임 변경할것 */} <div className="songs"> {/* 서열 9부터 0까지 내림차순으로 반환함 */} { levelIndex.map((detailDifficulty, index) => { // 서열값이 있는지 확인하고 있으면 JSX 출력, 없으면 null 뱉기 return list.filter(songlist => songlist.rank === detailDifficulty[0] || songlist.rank === detailDifficulty[1]).length !== 0 ? <div className="flexbox" key={index}> <h1 className="marginX header-margin test-width">{selectedDifficulty}.<span className="order-nums">{detailDifficultyFilter(detailDifficulty[0])}</span></h1> <div className='orderlist flexbox flex-wrap w100'> {/* 특정 서열(ex:19.최상 → 19.9과 19.8)에 해당하는 곡명과 이미지들 전부 출력 */} { list.filter(songlist => songlist.rank === detailDifficulty[0] || songlist.rank === detailDifficulty[1]).map((el, index)=>{ return ( <div className="songlist" key={index}> <div className="songbox"> <div className="imgbox"> < img src={process.env.PUBLIC_URL + '/music_disk/'+ el.name + '.webp'} alt={el.name}></img> {/* 스켈레톤 UI를 넣고 싶은데 어떻게 해야 할 지 모르겠음 <Skeleton width={"120px"} height={"120px"} circle={true} /> */} <div className="imgShadow"></div> <span className={`level-badge ${el.difficulty}`}>{el.difficulty}</span> </div> { state.rankUserSelected.songTitleView === true ? <p className="song-title">{el.name} </p> : null } </div> </div> ) }) } </div> </div> : null }) } </div> </div> ) }
export default RankOrderList ------------- store.js
import { createSlice, configureStore } from '@reduxjs/toolkit'
let rankUserSelected = createSlice({ name : "rankUserSelected", initialState : {selectedKey : "", selectedDifficulty : 0, songTitleView : true, isDescending : true}, reducers : { setTitleView(state, action){ state.songTitleView = action.payload }, setDescending(state, action){ state.isDescending = action.payload }, setKeyAndDifficulty(state, action){ state.selectedKey = action.payload.key; state.selectedDifficulty = action.payload.difficulty; }, } })
let selectIndex = createSlice({ name : "selectIndex", initialState : [ {key: '4k', difficulty : [19, 18, 17, 16], class: ["", "", "", ""]}, {key: '5k', difficulty : [20, 19, 18, 17], class: ["", "", "", ""]}, {key: '6k', difficulty : [20, 19, 18, 17], class: ["", "", "", ""]}, {key: '8k', difficulty : [20, 19, 18, 17], class: ["", "", "", ""]}], reducers : { setClass(state, action){ state.class = action.payload } } })
export default configureStore({ reducer: { rankUserSelected : rankUserSelected.reducer, selectIndex : selectIndex.reducer } })
export let { setTitleView, setKeyAndDifficulty, setDescending } = rankUserSelected.actions export let { setClass } = selectIndex.actions
---------------------- App.js
import 'bootstrap/dist/css/bootstrap.min.css'; import './App.css';
import { Routes, Route, useNavigate, Outlet } from "react-router-dom" import { Navbar, Container, Nav } from "react-bootstrap"
import Main from "./routes/Main.js" import RankOrderSelector from './routes/RankOrderSelector'; import RankOrderList from './routes/RankOrderList.js'; import Footer from './routes/Footer.js'
function App() { let navigate = useNavigate()
return ( <> {/* 네비게이션 영역 */} <Navbar bg="dark" variant="dark" className="z-index-10 pp-gradient pp-navbar-shadow"> <Container> <Navbar.Brand href="#home" onClick={()=>{ navigate('/') }}>EZ2ARCHIVE</Navbar.Brand> <Nav className="me-auto"> <Nav.Link href="#!" onClick={()=>{ navigate('')}} >성과표</Nav.Link> <Nav.Link href="#!" onClick={()=>{ navigate('/rank')}} >서열표</Nav.Link> <Nav.Link href="#!" onClick={()=>{ navigate('')}} >티어표</Nav.Link> <Nav.Link href="#!" onClick={()=>{ navigate('')}} >안내</Nav.Link> </Nav> <Nav> <Nav.Link href="#!" onClick={()=>{ navigate('')}} >로그인</Nav.Link> </Nav> </Container> </Navbar> <Container> {/* 라우트 영역 */} <Routes> <Route path="/" element={ <Main/> }></Route> <Route path="/rank" element={ <><RankOrderSelector/> <Outlet></Outlet></>}> <Route path="4K/:selectedDifficulty" element={ <><RankOrderList selectedKey="4K"/></> }></Route> <Route path="5K/:selectedDifficulty" element={ <><RankOrderList selectedKey="5K"/></> }></Route> <Route path="6K/:selectedDifficulty" element={ <><RankOrderList selectedKey="6K"/></> }></Route> <Route path="8K/:selectedDifficulty" element={ <><RankOrderList selectedKey="8K"/></> }></Route> </Route> </Routes> {/* 푸터 영역 */} <Footer/> </Container> </> ); }
export default App;
2023년 1월 31일 20:08 #66232
codingapple키 마스터lv18 글자엔 className={ state가 lv18이면 클래스명 붙여주고 아니면 떼주세요 } lv19 글자엔 className={ state가 lv19이면 클래스명 붙여주고 아니면 떼주세요 } ... 계속 코드 짜면 됩니다
-
글쓴이글
2 글 보임 - 1 에서 2 까지 (총 2 중에서)
- 답변은 로그인 후 가능합니다.