WEB/REACT

[REACT] 프로그래스바 완료 상태에 따라 transition줘서 움직이게 하기

자바칩 프라푸치노 2021. 6. 30. 12:17

 


(1) 버킷 리덕스에 상태값 넣기

완료하기 버튼

 

(2) 완료하기 버튼을 단다.

(3) 액션 만든다.

(4) 액션 생성함수를 만든다

(5) 리듀서를 만든다

 

(6) 버튼을 누르면 액션을 호출한다.

 

 

 

 

 

 


완료했으면 색을 바꿔주자!

 

BucketList.js에서

completed라는 변수를 ItemStyle에 넘겨주자

 

props로 completed값을 받아와서 완료됐으면 색을 바꿔주도록 한다.

 

 

 

 

 


프로그래스바를 만들어주자!!

이렇게 생긴 친구를 만든다

 

안에 있는 보라색은 완료한 비율에 따라서 width값이 바껴야하니까

 

 

이렇게 count라는 변수를 만들어주고

 

HighLight에 width값을 넘겨주고

HighLight에서 props로 받아온다.

 

 

 

 

 

 

 

코드보기
App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import React from "react";
 
import { withRouter } from "react-router";
import { Route, Switch } from "react-router-dom";
 
import BucketList from "./BucketList";
import styled from "styled-components";
import Detail from "./Detail";
import NotFound from "./NotFound";
import Progress from "./Progress";
import {connect} from "react-redux";
import { loadBucket, createBucket } from "./redux/modules/bucket";
 
// 리덕스에 있는 상태값을 props형태로 app에 넣어줌
const mapStateToProps = (state)=>{
  return {bucket_list : state.bucket.list};
 
}
 
// 액션 생성을 감시하는 것 액션 반환 .
const mapDispatchToProps = (dispatch)=>{
  return{
    load: ()=>{
      dispatch(loadBucket());
    },
 
    create: (bucket) =>{
      dispatch(createBucket(bucket));
    }
  };
}
 
class App extends React.Component {
  constructor(props) {
    super(props);
 
    this.state = {
      
    };
 
    this.text = React.createRef();
  }
 
  componentDidMount() {
    console.log(this.props);
  }
 
  addBucketList = () => {
    const new_item = this.text.current.value;
    this.props.create(new_item);
   
  };
 
  render() {
    return (
      <div className="App">
        <Container>
          <Title>내 버킷리스트</Title>
          <Progress/>
          <Line />
          <Switch>
          <Route
            path="/"
            exact
            render={(props) => 
            <BucketList 
            bucket_list={this.props.bucket_list} 
            history={this.props.history}/>}
          />
          {/* detail 페이지에서 버킷리스트를 보여주기 위해 
          몇 번째인지 알아야하기 때문에 index값을 가져온다 */}
          <Route path="/detail:index" component={Detail}/>
          <Route render={(props) => (
                <NotFound
                  history={this.props.history}
                />
              )}/>
          </Switch>
        </Container>
      
        <Input>
          <input type="text" ref={this.text} />
          <button onClick={this.addBucketList}>추가하기</button>
        </Input>
        <button onClick={()=>{
          window.scrollTo({top:0,left:0, behavior:"smooth"});
        }}>위로가기</button>
      </div>
    );
  }
}
 
const Input = styled.div`
  max-width: 350px;
  min-height: 10vh;
  background-color: #fff;
  padding: 16px;
  margin: 20px auto;
  border-radius: 5px;
  border: 1px solid #ddd;
  display:flex;
  align-items:center;
  justify-content: space-between;
  & *{
    padding: 5px;
  }
  & input {
    width: 70%;
    &:focus{
      border : 1px solid #673ab7;
    }
  }
  & button{
    width: 25%;
    color: #fff;
    border: 1px solid #874ecf;
    background-color:#874ecf; 
  }
`;
 
const Container = styled.div`
  max-width: 350px;
  min-height: 60vh;
  background-color: #fff;
  padding: 16px;
  margin: 20px auto;
  border-radius: 5px;
  border: 1px solid #ddd;
`;
 
const Title = styled.h1`
  color: slateblue;
  text-align: center;
`;
 
const Line = styled.hr`
  margin: 16px 0px;
  border: 1px dotted #ddd;
`;
// withRouter 적용
// connect로 컴포넌트에 연결
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(App));
cs
bucket.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// Action만들기
const LOAD   = 'bucket/LOAD';
const CREATE = 'bucket/CREATE';
const DELETE = 'bucket/DELETE';
const UPDATE = 'bucket/UPDATE';
// 초기값
const initialState = {
    list: [
    { text: "영화관 가기", completed: false },
    { text: "매일 책읽기", completed: false },
    { text: "수영 배우기", completed: false },
],
};
 
// Action 생성함수
// 액션을 반환해준다.
// 이 액션함수들을 컴포넌트에서 불러올 수 있게 export를 붙여준다.
// 여기서는 bucket을 안줘도 됨
export const loadBucket = (bucket)=>{
    return {type:LOAD, bucket};
}
 
// 무슨 데이터 추가할지 데이터도 들어가야한다.
// 여기서는 input 박스에서 텍스트를 가지고 와서 그 데이터를 추가한다.
// bucket이 그 텍스트이다.
export const createBucket= (bucket)=>{
    return {type: CREATE, bucket}
}
 
export const deleteBucket= (bucket)=>{
    return {type: DELETE, bucket}
}
 
export const updateBucket= (bucket)=>{
    return {type: UPDATE, bucket}
}
 
// Reducer
// 이 파일에서는 기본적으로 이 리듀서를 export해준다.
export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
     case "bucket/LOAD":{
        //  여기서는 기본값을 로드해오면 됨
         return state;
     }
     case "bucket/CREATE":{
        const new_bucket_list = [...state.list, {text: action.bucket, completed: false }];
        return {list: new_bucket_list};
    }
    case "bucket/DELETE":{
        // 원래 인덱스랑 바뀐 인덱스가 같으면 안바꾸고 다르면 바꾼다.
        const bucket_list = state.list.filter((l, idx)=>{
            if(idx != action.bucket){
                return l;
            }
        });
        return {list: bucket_list};
    }
 
    case "bucket/UPDATE":{
        const bucket_list = state.list.map((l, idx)=>{
            if(idx === action.bucket){
                return {...l, completed:true};
            }else{
                return l;
            }
        });
        return {list: bucket_list};
    }
    default:
        return state;
  }
}
 
 
cs
BucketList.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 리액트 패키지를 불러옵니다.
import React from "react";
import styled from "styled-components";
 
// redux hook을 불러옵니다.
import {useDispatch, useSelector} from 'react-redux';
 
const BucketList = (props) => {
  // 버킷리스트를 리덕스 훅으로 가져오기
  const bucket_list = useSelector(state => state.bucket.list);
 
  console.log(bucket_list);
  
  return (
    <ListStyle>
      {bucket_list.map((list, index) => {
        return (
          <ItemStyle
            className="list_item"
            key={index}
            completed={list.completed}
            onClick={() => {
              // 배열의 몇번째 항목을 눌렀는 지, url 파라미터로 넘겨줍니다.
              props.history.push("/detail"+index);
            }}
          >
            {list.text}
          </ItemStyle>
        );
      })}
    </ListStyle>
  );
};
 
const ListStyle = styled.div`
  display: flex;
  flex-direction: column;
  height: 50vh;
  overflow-x: hidden;
  overflow-y: auto;
`;
 
const ItemStyle = styled.div`
  padding: 16px;
  margin: 8px;
  font-weight: 600;
  color: ${(props)=>(props.completed?"#fff""black")};
  background-color: ${(props)=> (props.completed? "#673ab7":"#eee")};
`;
 
export default BucketList;
cs
Progress.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import React from "react";
import styled from "styled-components";
import { useSelector } from "react-redux";
 
const Progress =(props)=>{
    const bucket_list = useSelector(state => state.bucket.list);
 
    let count = 0;
    bucket_list.map((l, idx)=>{
        if(l.completed){
            count++;
        }
    })
    return (
        <ProgressBar>
 
            <HighLight width={(count/bucket_list.length)*100 +"%"}/>
            <Dot/>
        </ProgressBar>
 
    );
}
 
const ProgressBar = styled.div`
    background: #eee;
    width: 100%;
    height: 20px;
    display:flex;
    align-items: center;
    border-radius: 10px;
`;
 
const HighLight = styled.div`
    background: #673ab7;
    height: 20px;
    width: ${props =>props.width};
    transition: width 1s;
    border-radius: 10px;
`;
 
const Dot = styled.div`
    background: #fff;
    border: 5px solid #673ab7;
    box-sizing: border-box;
    width: 40px;
    height: 40px;
    border-radius: 20px;
    margin: 0px 0px 0px -10px;
`;
 
export default Progress;
cs
Detail.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 리액트 패키지를 불러옵니다.
import React from "react";
 
// redux hook을 불러옵니다.
import { useDispatch, useSelector } from "react-redux";
// 내가 만든 액션 생성 함수를 불러옵니다.
import {deleteBucket,updateBucket} from "./redux/modules/bucket";
 
const Detail = (props) => {
    const dispatch = useDispatch();
    
    
  // 스토어에서 상태값 가져오기
  const bucket_list = useSelector((state) => state.bucket.list);
  // url 파라미터에서 인덱스 가져오기
  let bucket_index = parseInt(props.match.params.index);
 
  
  return (
    <div>
      <h1>{bucket_list[bucket_index].text}</h1>
      <button onClick={() => {
        dispatch(deleteBucket(bucket_index));
        props.history.goBack();
      }}>삭제하기</button>
      <button onClick={()=>{
        dispatch(updateBucket(bucket_index));
        props.history.goBack();
      }}>완료하기</button>
    </div>
  );
};
 
export default Detail;
cs
728x90