[react-beautiful-dnd] react, next.js 드래그 만들기 2

 

[react-beautiful-dnd] react, next.js 드래그 만들기 2

[react-beautiful-dnd] react, next.js 드래그 만들기 1 [react-beautiful-dnd] react, next.js 드래그 만들기 1 # react-beutiful-dnd 요소를 통한 Drag 만들기 github.com/atlassian/react-beautiful-dnd atlass..

minaminaworld.tistory.com

# 결과화면 


 

1.  index.js

 - DragLand Component 불러오기

import DragLand from "../components/DragComponent/DragLand"
// import Test from '../components/Test'

const Index = () => {
    return (
        <>
            <div>Hello next.js 10</div>
            <br/>
            {/* Drag Component */}
            <DragLand></DragLand>
        </>
    )
}

export default Index;

 

2.  DragLand.js

 -DragLand.js ComponentLoad된 시점 Drag Components 불러오기

// React 관련 요소
import React, { useCallback, useEffect, useState, PureComponent } from 'react';
import Drag from './Drag';

const DragLand = () => {
    // window가 로드 된 시점에서 렌더링
    const [winReady, setwinReady] = useState(false);
    useEffect(() => {
        setwinReady(true);
    }, []);

    return (
        <>
            {/* 윈도우, DOM 로드 시점에서 드래그 생성 */}
            {winReady ? <Drag /> : null}
        </>
    );
};

export default DragLand;

3.  Drag.js

 -Drag 요소 만들기

// React 관련 요소
import React, { useCallback, useEffect, useState, PureComponent } from 'react';
// 드래그 요소
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
// 스타일 정의 
import styled from 'styled-components';
import '@atlaskit/css-reset';

const DragContent = styled.div`
  border: 1px solid lightgrey;
`;

const Content = styled.div`
  margin: 8px;
  padding : 10px;
  border: 1px solid lightgrey;
  border-radius: 2px;
`;

// 드래그 요소 생성 
const Drag = () => {
    const [datas, setDatas] = useState([
        { key: 'item-1', content: 'item-1' },
        { key: 'item-2', content: 'item-2' },
        { key: 'item-3', content: 'item-3' },
        { key: 'item-4', content: 'item-4' },
    ]);

    const onDragEnd = (result, provided) => {

        if (!result) {
            console.log('result가 null인 경우');
            return;
        }

        // 드래그 결과
        // source : 원본 
        // destination : 변경 
        const { destination, source } = result;

        // 동일한 위치에서 놓은 경우 
        if (destination.index === source.index) {
            console.log('초기 위치 index 동일한 경우');
            return;
        }

        // 데이터 변경
        setDatas((prev) => {
            // 원본 데이터 
            const sourceData = datas[source.index];
            // datas 복사
            let newDatas = prev;
            // 기존 데이터 제거 
            newDatas.splice(source.index, 1);
            // 이동 위치로 데이터 옮기기
            newDatas.splice(destination.index, 0, sourceData);

            return newDatas;
        });

    };

    return (
        <>
            {/* 드래그 영역 */}
            <DragDropContext
                onDragEnd={onDragEnd}
            >
                {/* 드래그 놓을 수 있는 영역 */}
                <Droppable droppableId="DropLand">
                    {/* 드래그 Div 생성 */}
                    {(provided, snapshot) => (
                        // CCS가 적용된 Div
                        <DragContent
                            {...provided.droppableProps}
                            ref={provided.innerRef}
                        >
                            <p>Drag Div!</p>
                            {datas.map((data, index) => (
                                <Draggable key={data.key} draggableId={data.key} index={index}>
                                    {(provided, snapshot) => (
                                        <Content
                                            ref={provided.innerRef}
                                            {...provided.draggableProps}
                                            {...provided.dragHandleProps}
                                        >
                                            {data.content}
                                        </Content>
                                    )}
                                </Draggable>
                            ))}
                            {provided.placeholder}
                        </DragContent>
                    )}
                </Droppable>
            </DragDropContext>
        </>
    )
}

export default Drag;

 

블로그 이미지

미나미나미

댓글을 달아 주세요

 

[react-beautiful-dnd] react, next.js 드래그 만들기 1

 

[react-beautiful-dnd] react, next.js 드래그 만들기 1

# react-beutiful-dnd 요소를 통한 Drag 만들기 github.com/atlassian/react-beautiful-dnd atlassian/react-beautiful-dnd Beautiful and accessible drag and drop for lists with React - atlassian/react-beau..

minaminaworld.tistory.com


1.   npm i styled-components

   - css 적용하기 위해서 설치 

2.  npm i react-beautiful-dnd

    - drag을 위해서 설치 

3. npm i @atlaskit/css-reset

   - drag 생성할 때, 필요한 요소라고 함. (공식 문서에서 추가 요소)

 

사용 방법

4. package.json

   - 설치된 요소

블로그 이미지

미나미나미

댓글을 달아 주세요

# react-beutiful-dnd 요소를 통한 Drag 만들기

github.com/atlassian/react-beautiful-dnd

 

atlassian/react-beautiful-dnd

Beautiful and accessible drag and drop for lists with React - atlassian/react-beautiful-dnd

github.com


1. package.json 생성 

2. react 요소 설치

 



3. next 설치 

4. next 실행

블로그 이미지

미나미나미

댓글을 달아 주세요

# 에러 상황

  - Next.js에서 react-beautiful-dnd을 적용할 때, 드래그가 제대로 동작하지 않은 경우.   

 

package.json

 

 

# 문제점 

 - 문제가 되는 이유는 컴포넌트가 로드 되기 전, DOM, Window가 먼저 로드되지 않는 상황이라고 판단됨.

 

 

 # 해결 방안 

 

  - useEffect를 통해서 Window, DOM이 렌더링 된 시점에서 드래그 요소 렌더링.

 

# 결과화면 

 

블로그 이미지

미나미나미

댓글을 달아 주세요

hooks 방식의 컴포넌트 생명 주기

# 맨 아래의 전체 소스 코드가 있습니다.


# 클래식 방식의 컴포넌트 주기 : minaminaworld.tistory.com/196

 

[react - 기초] class 방식의 컴포넌트 생명 주기

class 방식의 컴포넌트 생명 주기 # 맨 아래의 전체 소스 코드가 있습니다.  # 리액트 공식문서 : https://ko.reactjs.org/docs/react-component.html React.Component – React A JavaScript library for b..

minaminaworld.tistory.com


# Hooks 방식의 경우, Class와는 다르게 componentDidMount, componentDidUpdate, componentWillUnmount 처럼 명확하게 나누어져 있지 않음. 그러나 useEffect만으로 구현이 가능함.

 // class 방식의 componentDidMout
  useEffect(() => {
    console.log('componentDidMout -------------------------');
    console.log('component가 실행될 때만 동작합니다.');
    console.log(' ------------------------- componentDidMout');
  }, []);

  // class 방식의 componentDidUpdate 동작
  // 배열 요소의 변경이 있을 때만 동작함
  useEffect(() => {
    console.log('componentDidUpdate -------------------------');
    // 값의 변화에 대응
    if (num) {
      console.log('num=', num);
    }
    console.log('-------------------------componentDidUpdate ');
  }, [num]);

  // class 방식의 componentDidUpdate 동작
  // 배열 요소의 변경이 있을 때만 동작함
  useEffect(() => {
    return () => {
      console.log('componentWillUnmount-------------------------');
      console.log('componentWillUnmount 동작함');
      console.log('-------------------------componentWillUnmount ');
    };
  }, [num]);

 

 

# 컴포넌트 시작 시 useEffect 실행.

# 값 변화로 인한 useEffect 실행.

# 값 변화로 인한 DOM의 종료로 인한 useEffect 실행.

 

 


# 전체소스코드

import React, { memo, useEffect, useState } from "react";

const useEffectTest = () => {
  const [title, setTitle] = useState("ComponentMountTest");
  const [num, setNum] = useState(0);

  // class 방식의 componentDidMout
  useEffect(() => {
    console.log('componentDidMout -------------------------');
    console.log('component가 실행될 때만 동작합니다.');
    console.log(' ------------------------- componentDidMout');
  }, []);

  // class 방식의 componentDidUpdate 동작
  // 배열 요소의 변경이 있을 때만 동작함
  useEffect(() => {
    console.log('componentDidUpdate -------------------------');
    // 값의 변화에 대응
    if (num) {
      console.log('num=', num);
    }
    console.log('-------------------------componentDidUpdate ');
  }, [num]);

  // class 방식의 componentDidUpdate 동작
  // 배열 요소의 변경이 있을 때만 동작함
  useEffect(() => {
    return () => {
      console.log('componentWillUnmount-------------------------');
      console.log('componentWillUnmount 동작함');
      console.log('-------------------------componentWillUnmount ');
    };
  }, [num]);

  const onIncreseNum = () => {
    console.log("onIncreseNum");
    setNum((prevNum) => {
      return prevNum + 1;
    });
  };

  const onDecreseNum = (sign) => (e) => {
    console.log("onDecreseNum");
    console.log(`sign ${sign}`);
    setNum((prevNum) => {
      return prevNum - 1;
    });
  };

  return (
    <>
      <h1>{title}</h1>
      <h1>{num}</h1>
      <button onClick={onIncreseNum}>+</button>
      <button onClick={onDecreseNum('-')}>-</button>
    </>
  );
};

export default useEffectTest;
블로그 이미지

미나미나미

댓글을 달아 주세요

class 방식의 컴포넌트 생명 주기

# 맨 아래의 전체 소스 코드가 있습니다.

 # 리액트 공식문서 : https://ko.reactjs.org/docs/react-component.html

 

React.Component – React

A JavaScript library for building user interfaces

ko.reactjs.org


# componentDidMount

 - 컴포넌트 시작시 실행됨.

    /*
        * 마운트 
            - 컴포넌트의 인스턴스가 생성되어 DOM 상에 삽입될 때에 순서대로 호출
        1. constructor()
        2. static getDerivedStateFromProps()
        3. render()
        4. componentDidMount
    */
    componentDidMount(){
        console.log('componentDidMount');
        console.log(arguments);
        console.log('컴포넌트가 처음 실행시 동작함');
    }


# componentDidUpdate

 /*
        https://ko.reactjs.org/docs/react-component.html
        * 업데이트
            - props 또는 state가 변경되면 갱신이 발생.
        1. static getDerivedStateFromProps()
        2. shouldComponentUpdata()
        3. render()
        4. 
    */
    componentDidUpdate(prevProps, prevState, snapshot){
        console.log('componentDidUpdate=======================');
        console.log(arguments);
        console.log("prevProps" , prevProps);
        console.log("prevState" , prevState);
        console.log("snapshot"  , snapshot);
        console.log('=======================componentDidUpdate');
    }

 

 


# componentWillUnmount

 - 파일 갱신으로 인한 componetDidMound 확인하기(Hot-loader module로 업데이트됨.)

    /*
        - 컴포넌트가 DOM 상에서 제거될 때에 호출됩니다.
    */
    componentWillUnmount(){
        // 화면이 끝나기전에 실행이됨.
        console.log('componentWillUnmount=======================');
        console.log(arguments);
        console.log('=======================componentWillUnmount');
    }


 

 

# 전체소스코드

import React, { Component } from 'react';


class ComponentMountTest extends Component{
    state = {
        title : 'ComponentMountTest',
        num : 0
    }; 

    /*
        * 마운트 
            - 컴포넌트의 인스턴스가 생성되어 DOM 상에 삽입될 때에 순서대로 호출
        1. constructor()
        2. static getDerivedStateFromProps()
        3. render()
        4. componentDidMount
    */
    componentDidMount(){
        console.log('componentDidMount');
        console.log(arguments);
        console.log('컴포넌트가 처음 실행시 동작함');
        console.log('aaa');
    }

    /*
        https://ko.reactjs.org/docs/react-component.html
        * 업데이트
            - props 또는 state가 변경되면 갱신이 발생.
        1. static getDerivedStateFromProps()
        2. shouldComponentUpdata()
        3. render()
        4. 
    */
    componentDidUpdate(prevProps, prevState, snapshot){
        console.log('componentDidUpdate=======================');
        console.log(arguments);
        console.log("prevProps" , prevProps);
        console.log("prevState" , prevState);
        console.log("snapshot"  , snapshot);
        console.log('=======================componentDidUpdate');
    }

    /*
        - 컴포넌트가 DOM 상에서 제거될 때에 호출됩니다.
    */
    componentWillUnmount(){
        // 화면이 끝나기전에 실행이됨.
        console.log('componentWillUnmount=======================');
        console.log(arguments);
        console.log('=======================componentWillUnmount');
    }

    onIncreseNum = () =>{
        this.setState((prev)=>{
            return {
                num : prev.num + 1
            } 
        })
    }

    onDecreseNum = () =>{
        this.setState((prev)=>{
            return {
                num : prev.num - 1
            } 
        })
    }

    render(){
        const {title, num} = this.state;
        return(
            <>
                <h1>{title}</h1>
                <h1>{num}</h1>
                <button onClick={this.onIncreseNum}>+</button>
                <button onClick={this.onDecreseNum}>-</button>
            </>
        )
    }

}

export default ComponentMountTest;
블로그 이미지

미나미나미

댓글을 달아 주세요

# 프로젝트 코드

github.com/HelloPlutoKid/javascriptCodeTest/tree/master/12.react/0.react-pratice/4.props

# 프로젝트 폴더 구조


# props로 전달된 데이터 표현 

props 전달된 데이터 표현


 

 

1. Hooks 방식으로 데이터 전달

  1.1. PropsParent.jsx

import React, { memo, useRef, useState, createRef } from 'react';
import PropsChild from '../component/PropsChild';

const PropsParent = () => {
    const [name, setName] = useState('props 전달');
    const [users, setUsers] = useState([
        { name: 'a', grade: '2' },
        { name: 'b', grade: '1' },
        { name: 'c', grade: '6' },
    ]);

    return (
        <>
            <div id='PropsParent'>
                <h4>PropsParent</h4>
                {/* 보낼이름={보낼 데이터} */}
                <PropsChild name={name} users={users}></PropsChild>
            </div>
        </>
    );
};
export default PropsParent;

  1.2. PropsChild.jsx

import React, { memo, useRef, useState, createRef } from 'react';

//  props로 전달받을 녀석 정의
const PropsChild = ({users , name}) =>{
    return(
        <>
            <div id='PropsChild'>
                <h4>PropsChild</h4>
                <div>{name}</div>
                {users.map((item, index) =>
                    <div key={index}>{index} {item.name} {item.grade}</div>
                )}
            </div>
        </>
    );
};

export default PropsChild;

2. class 방식으로 데이터 전달

  2.1. PropsParent.jsx

import React, { Component } from 'react';
import PropsChild from './PropsChild'

class PropsParent extends Component {
    state = {
        name: 'props 전달 name name',
        users: [
            { name: 'a', grade: '2' },
            { name: 'b', grade: '1' },
            { name: 'c', grade: '6' },
        ]
    };

    render() {
        const { name, users } = this.state;
        return (
            <>
                <div id='PropsParent'>
                    <h4>PropsParent</h4>
                    {/* 보낼이름={보낼 데이터} */}
                    <PropsChild name={name} users={users}></PropsChild>
                </div>
            </>
        );
    }
}

export default PropsParent;

 

 

2.2. PropsChild.jsx

import React, { Component } from 'react';

class PropsChild extends Component{
    render(){
        const {users , name} = this.props;
        return(
            <>
                <div id='PropsChild'>
                    <h4>PropsChild</h4>
                    {/* this.props로 받은 데이터 표현하기 */}
                    <div>{name}</div>
                    {/* this.props로 받은 데이터 출력하기 */}
                    {users.map((item, index) =>
                        <div key={index}>{index} {item.name} {item.grade}</div>
                    )}
                </div>
            </>
        );
    }
}

export default PropsChild;

3. client.jsx

// import 방법과 const 방법 . 
// const React = require('react');
import React from 'react';

// const ReactDom = require('react-dom');
import ReactDom from 'react-dom';

// const { hot } = require('react-hot-loader/root');
import { hot } from 'react-hot-loader/root';

// 불러올 컴포넌트의 이름
import PropsParent from './component/PropsParent' 
const Hot = hot(PropsParent);

ReactDom.render(<Hot />, document.querySelector('#root'));


4. webpack.config.js

const path = require('path');
const webpack = require('webpack');
// process.env.NODE_ENV = 'production';

module.exports = {
    // 프로젝트 이름 , 카멜 케이스는 안됨, 두 단어 이상을 사용한 프로젝트 이름 
    name: 'props-test', 
    mode: 'development', // 실서비스 : productuon
    // mode: 'productuon', // 실서비스 : productuon
    devtool: 'eval', // 빠르게
    resolve: {
        // 파일 형식 
        extensions: ['.js', '.jsx']
    },
    entry: {
        app: ['./client']
    }, // 입력
    module: {
        rules: [{
            test: /\.jsx?/,
            loader: 'babel-loader',
            options: {
                presets: [
                    ['@babel/preset-env', {
                        targets: {
                            browsers: ['> 5% in KR']
                        },
                        debug: true,
                    }],
                    '@babel/preset-react'],
                plugins: [
                    '@babel/plugin-syntax-class-properties',
                    "transform-class-properties",
                    'react-hot-loader/babel'
                ]
            }
        }]
    },
    plugins: [
        new webpack.LoaderOptionsPlugin({ debug: true }),
    ],
    output: {
        // webpack-dev-server에서 hot 로드 되기 위해서 위치 지정, 없을 경우 html에서 ./dist/app.js를 app.js로 변경해야함
        publicPath: '/dist/',
        path: path.join(__dirname, 'dist'),
        filename: 'app.js'
    }, // 출력
    devServer: {
        // contentBase: path.join(__dirname, "dist"),
        inline: true,
        hot: true,
        port: 9000,
        hot:true
    },

}

 

 

5. index.html(style 내용 확인)

 

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>props 테스트 하기</title>
    <style>
        div{
            font-size: 30px;
            font-weight: bold;
        }

        #PropsParent{
            border: 1px solid red;
            padding : 10px;
        }

        #PropsChild{
            border: 1px solid blue;
            padding : 10px;
        }
    </style>
</head>
<body>
    <div id="root"></div>
    <script src="./dist/app.js"></script>
</body>
</html>
블로그 이미지

미나미나미

댓글을 달아 주세요

# 폴더 구조


class 방식으로 버튼 사용하기 


1. class 방식에 버튼 이벤트

// class 방식일 경우 
import React, { Component, PureComponent } from 'react';
// import React, {memo, useRef, useState} from 'react';


class btnTest extends Component {
    state = {
        text: 'state에서 text 알아보기',
        num: 0,
        inputVal : 'state input Val'
    };
    
    // input 사용할 때, 사용될 부분 
    input;
    onInputValChange = (e) => this.setState({ inputVal: e.target.value });
    onRefInput = (c) => {this.input = c;}
    // input 사용할 때, 사용될 부분 

     onNumBtn = (sign) => (e) => {
        if (sign === '+') {
            // setState에서는 값을 직접적으로 참조가 불가능함으로 함수로 리턴 형식으로 함.
            this.setState((pre) => {
                console.log(pre);
                return {
                    text: '증감',
                    num: pre.num + 1
                }
            });
        } else {
            this.setState((pre) => {
                console.log(pre);
                return {
                    text: '감소',
                    num: pre.num - 1
                }
            });
        }
    };

    render() {
        const { text, num ,inputVal } = this.state;
        return (
            <>
                <h2 id='test'>{text}</h2>
                <h2 id='num'>{num}</h2>
                <div>
                    <button onClick={this.onNumBtn('+')}>+</button>
                    <button onClick={this.onNumBtn('-')}>-</button>
                </div>
                <input type='text' value={inputVal} onChange={this.onInputValChange} ref={this.onRefInput}></input>
                <h1>{inputVal}</h1>
            </>
        );
    };
}
export default btnTest;

 

 

2. Hooks 방식에 버튼 이벤트

// class 방식일 경우 
// import React, { Component, PureComponent } from 'react';
import React, { memo, useEffect, useRef, useState } from 'react';

const btnTest = memo(() => {
    const [text, setText] = useState('Hooks Text Display');
    const [num, setNum] = useState(0);
    const [inputVal, setInputVal] = useState('input text');
    const input = useRef();

	const onNumBtn = (sign) => (e) => {
        if (sign === '+') {
            setText('증감');
            setNum((pre) => {
                return pre + 1;
            });
        } else {
            setText('감소');
            setNum((pre) => {
                return pre - 1;
            });
        }
    };

    const onInputValChange = (e) => {
        setInputVal(e.target.value);
    };

    return (
        <>
            <h2 id='test'>{text}</h2>
            <h2 id='num'>{num}</h2>
            <div>
                <button onClick={onNumBtn('+')}>+</button>
                <button onClick={onNumBtn('-')}>-</button>
            </div>
            <input type='text' value={inputVal} onChange={onInputValChange} ref={input}></input>
            <h1>{inputVal}</h1>
        </>
    );
});

export default btnTest;

3. client.jsx

import React from 'react';

// # https://ko.reactjs.org/docs/react-dom.html
// react-dom package는 앱의 최상위 레벨에서 사용할 수 있는 DOM에 특화된 메서드와 필요한 경우 React 모델 외부로 나갈 수 있는 해결책을 제공
// render(), hydrate(), unmountComponentAtNode(), findDOMNode(), createPortal()
// const ReactDom = require('react-dom');
import ReactDom from 'react-dom';

// # https://gist.github.com/velopert/21896de87566628b38b7ecb95973a7b3
// 코드가 변경되었을 때 페이지를 새로고침하지 않고 바뀐 부분만 빠르게 교체해주는 라이브러리
// const { hot } = require('react-hot-loader/root');
import { hot } from 'react-hot-loader/root';

// 불러올 컴포넌트의 이름
import btnTest from './component/btnTest';
const Hot = hot(btnTest);

ReactDom.render(<Hot />, document.querySelector('#root'));


 

 

4. webpack.config.js

const path = require('path');
const webpack = require('webpack');
// process.env.NODE_ENV = 'production';

module.exports = {
    // 프로젝트 이름 , 카멜 케이스는 안됨, 두 단어 이상을 사용한 프로젝트 이름 
    name: 'button-test', 
    mode: 'development', // 실서비스 : productuon
    // mode: 'productuon', // 실서비스 : productuon
    devtool: 'eval', // 빠르게
    resolve: {
        // 파일 형식 
        extensions: ['.js', '.jsx']
    },
    entry: {
        app: ['./client']
    }, // 입력
    module: {
        rules: [{
            test: /\.jsx?/,
            loader: 'babel-loader',
            options: {
                presets: [
                    ['@babel/preset-env', {
                        targets: {
                            browsers: ['> 5% in KR']
                        },
                        debug: true,
                    }],
                    '@babel/preset-react'],
                plugins: [
                    '@babel/plugin-syntax-class-properties',
                    "transform-class-properties",
                    'react-hot-loader/babel'
                ]
            }
        }]
    },
    plugins: [
        new webpack.LoaderOptionsPlugin({ debug: true }),
    ],
    output: {
        // webpack-dev-server에서 hot 로드 되기 위해서 위치 지정, 없을 경우 html에서 ./dist/app.js를 app.js로 변경해야함
        publicPath: '/dist/',
        path: path.join(__dirname, 'dist'),
        filename: 'app.js'
    }, // 출력
    devServer: {
        // contentBase: path.join(__dirname, "dist"),
        inline: true,
        hot: true,
        port: 9000,
        hot:true
    },

}

5. 전체소스코드 

 

# github.com/HelloPlutoKid/javascriptCodeTest/tree/master/12.react/0.react-pratice/2.function

 

HelloPlutoKid/javascriptCodeTest

Contribute to HelloPlutoKid/javascriptCodeTest development by creating an account on GitHub.

github.com

 

블로그 이미지

미나미나미

댓글을 달아 주세요