【React】Reactバージョン18でドラッグ&ドロップ

今回はReactのバージョン18で、ドラッグ&ドロップを実現する方法を書いていきます。

react-beautiful-dndを利用します。

アプリ作成

長いのでさっそくはじめます。任意のフォルダにReactアプリを作成します。

npx create-react-app myapp

cd myapp

yarn start

react

App.jsの不要なコードを削除して、Hello worldを表示するように記述します。

import './App.css';


function App() {
return (
<div className="App">
<h2>Hello world</h2>
</div>
);
}


export default App;
ブラウザの表示はこのようになります。
react

データ作成

initial-Data.jsファイルを作成して、データを作成します。
const initialData = {
tasks: {
'task-1': { id: 'task-1', content: '掃除をする' },
'task-2': { id: 'task-2', content: '洗濯をする' },
'task-3': { id: 'task-3', content: '買い物をする' },
'task-4': { id: 'task-4', content: '料理をする' },
'task-5': { id: 'task-5', content: '運動をする' },
},
columns: {
'column-1': {
id: 'column-1',
title: 'To do',
taskIds: ['task-1', 'task-2', 'task-3', 'task-4', 'task-5' ],
},
},
columnOrder: ['column-1'],
};


export default initialData;

App.js

App.jsに作成したデータをimportします。
import initialData from './initial-data';
App.jsをclass形式に書き換え、次のように記述します。
import React from 'react';
import './App.css';
import initialData from './initial-data';


class App extends React.Component {
state = initialData;


render() {
return this.state.columnOrder.map((columnId) => {
const column = this.state.columns[columnId];
const tasks = column.taskIds.map(taskId => this.state.tasks[taskId]);


return column.title;


});
}
}


export default App;
この時点でブラウザには、columnsのtitleであるTo doが表示されます。
表示されない場合は、コードの打ち間違えがないか確認しましょう。
react drag

column.jsx

column.jsxファイルを作成して、次のように記述します。
import React from 'react';


export default class Column extends React.Component {
render() {
return this.props.column.title;
}
}

App.js

App.jsで、作成したcolumn.jsxファイルをインポートして、<Column />をreturnします。
次のように書き換えましょう。
import React from 'react';
import './App.css';
import initialData from './initial-data';
import Column from './column';


class App extends React.Component {
state = initialData;


render() {
return this.state.columnOrder.map((columnId) => {
const column = this.state.columns[columnId];
const tasks = column.taskIds.map(taskId => this.state.tasks[taskId]);


return <Column key={column.id} column={column} tasks={tasks} />;


});
}
}


export default App;

スタイルシート

次にスタイルシートを定義していきますので、App.cssのインポート(import './App.css';)は削除して、https://atlassian.design/のスタイルシートをインストールします。
次のコマンドを打ちます。
yarn add @atlaskit/css-reset
column.jsxファイルでスタイルを定義していきます。
styled-componentsをインストールします。
yarn add styled-components
column.jsxファイルに次のように記述します。
`はバックティックですので、ご注意ください。
import React from 'react';
import styled from 'styled-components';


const Container = styled.div``;
const Title = styled.h3``;
const TaskList = styled.div``;


export default class Column extends React.Component {
render() {
return (
<Container>
<Title>{this.props.column.title}</Title>
<TaskList>タスクリスト</TaskList>
</Container>
)
}
}
ブラウザで見ると、現在はまだこのような表示です。
react drag
column.jsxファイルに、下記のコード加えます。
import React from 'react';
import styled from 'styled-components';


const Container = styled.div`
margin: 8px;
border: 1px solid lightgrey;
border-radius: 2px;
`;
const Title = styled.h3`
padding: 8px;
`;
const TaskList = styled.div`
padding: 8px;
`;
ブラウザにスタイルが適用されます。
react drag

task.jsx

次にtask.jsxファイルを作成して、次のように記述します。
import React from 'react';
import styled from 'styled-components';


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


export default class Task extends React.Component {
render() {
return <Container>{this.props.task.content}</Container>;
}
}

colum.jsx

作成したTaskコンポーネントをcolum.jsxでインポートして利用します。
import React from 'react';
import styled from 'styled-components';
import Task from './task';


const Container = styled.div`
margin: 8px;
border: 1px solid lightgrey;
border-radius: 2px;
`;
const Title = styled.h3`
padding: 8px;
`;
const TaskList = styled.div`
padding: 8px;
`;


export default class Column extends React.Component {
render() {
return (
<Container>
<Title>{this.props.column.title}</Title>
<TaskList>
{this.props.tasks.map(task => <Task key={task.id} task={task} />)}
</TaskList>
</Container>
)
}
}

ブラウザにTo do表示

ブラウザではこのように表示されます。
react drag

react-beautiful-dndインストール

react-beautiful-dndをインストールします。
yarn add react-beautiful-dnd

react-beautiful-dndの構成要素

react-beautiful-dndは、次の3つの要素で構成されます。
DragDropContext
Droppable
Draggable

DragDropContext

App.jsファイルで、DragDropContextを定義します。次のように記述します。
import React from 'react';
import initialData from './initial-data';
import Column from './column';
import '@atlaskit/css-reset';
import { DragDropContext } from 'react-beautiful-dnd';


class App extends React.Component {
state = initialData;


onDragEnd = result => {


}


render() {
return (
<DragDropContext onDragEnd={this.onDragEnd}>{this.state.columnOrder.map((columnId) => {
const column = this.state.columns[columnId];
const tasks = column.taskIds.map(taskId => this.state.tasks[taskId]);


return <Column key={column.id} column={column} tasks={tasks} />;


})}
</DragDropContext>
);
}
}


export default App;

Droppable

column.jsxにDroppableを定義します。

import React from 'react';
import styled from 'styled-components';
import Task from './task';
import { Droppable } from 'react-beautiful-dnd';


const Container = styled.div`
margin: 8px;
border: 1px solid lightgrey;
border-radius: 2px;
`;
const Title = styled.h3`
padding: 8px;
`;
const TaskList = styled.div`
padding: 8px;
`;


export default class Column extends React.Component {
render() {
return (
<Container>
<Title>{this.props.column.title}</Title>
<Droppable droppableId={this.props.column.id}>
{provided => (
<TaskList
ref={provided.innerRef}
{...provided.droppableProps}
>
{this.props.tasks.map((task, index) => 
<Task key={task.id} task={task} index={index} />)}
{provided.placeholder}
</TaskList>
)}
</Droppable>
</Container>
)
}
}

Draggable

task.jsxファイルにDraggableを適用します。
import React from 'react';
import styled from 'styled-components';
import { Draggable } from 'react-beautiful-dnd';


const Container = styled.div`
border: 1px solid lightgrey;
border-radius: 2px;
padding: 8px;
margin-bottom: 8px;
background-color: white;
`;


export default class Task extends React.Component {
render() {
return (
<Draggable draggableId={this.props.task.id} index={this.props.index}>
{(provided) => (
<Container
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
{this.props.task.content}</Container>
)}
</Draggable>
);
}
}

<React.StrictMode>をコメントアウト

Reactのバージョン18では、エラーが発生してドラッグできませんので、index.jsファイルの

<React.StrictMode>をコメントアウトします。
import React from 'react';
import ReactDOM from 'react-dom/client';
import reportWebVitals from './reportWebVitals';
import './index.css';
import App from './App';



const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
//<React.StrictMode>
<App />
//</React.StrictMode>
);


// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals(console.log);

ドラッグ&ドロップ

これでブラウザに戻り、ドラッグ&ドロップを試しましょう。
現状ではまだ完成ではありません。
試してみるとわかりますが、ドラッグ&ドロップした後に、リストの表示が初期の状態に戻ってしまいます。
現在は次のような動きになっています。
react drag&drop

onDragEnd

解決するには、onDragEndにロジックを定義する必要がある為、App.jsに次のように書き加えます。
import React from 'react';
import initialData from './initial-data';
import Column from './column';
import '@atlaskit/css-reset';
import { DragDropContext } from 'react-beautiful-dnd';


class App extends React.Component {
state = initialData;


onDragEnd = result => {
const { destination, source, draggableId } = result;


if (!destination) {
return;
}


if (
destination.droppableId === source.droppableId &&
destination.index === source.index
) {
return;
}


const column = this.state.columns[source.droppableId];
const newTaskIds = Array.from(column.taskIds);
newTaskIds.splice(source.index, 1);
newTaskIds.splice(destination.index, 0, draggableId);


const newColumn = {
...column,
taskIds: newTaskIds,
};


const newState = {
...this.state,
columns: {
...this.state.columns,
[newColumn.id]: newColumn,
},
};


this.setState(newState);
};


render() {
return (
<DragDropContext onDragEnd={this.onDragEnd}>{this.state.columnOrder.map((columnId) => {
const column = this.state.columns[columnId];
const tasks = column.taskIds.map(taskId => this.state.tasks[taskId]);


return <Column key={column.id} column={column} tasks={tasks} />;


})}
</DragDropContext>
);
}
}


export default App;

完成

これで完成です。
ドラッグ&ドロップが実現できています。
react drag and drop

まとめ

今回はReactのバージョン18で、ドラッグ&ドロップを実現する方法を書きました。
最後までお読みいただきありがとうございました。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください