- vừa được xem lúc

Xây dựng trình soạn thảo văn bản trong React với Draft Js

0 0 46

Người đăng: Phan Văn Tấn

Theo Viblo Asia

Hey yo, chào tất cả các bạn ?
Lại là mình đây, Tấn đẹp trai từ bé hay chia sẻ công nghệ với mọi người vào mỗi tháng đây.
Hôm nay thì mình sẽ chia sẻ với các bạn về một package đã làm đau đầu mình trong một thời gian rất là dài.
À mà hiện tại mình vẫn còn đau đầu vì nó. Vâng đó chính là Draft js - một con hàng đến từ ông lớn Facebook.

Tổng quan

Draft.js là một framework để xây dựng các trình soạn thảo văn bản trong React, được cung cấp bởi một mô hình immutableabstracting dựa trên sự khác biệt giữa các trình duyệt.

Draft.js cho phép bạn tạo bất kỳ kiểu nhập văn bản nào, cho dù bạn chỉ muốn hỗ trợ một vài kiểu văn bản trên dòng hay xây dựng một trình soạn thảo văn bản phức tạp để soạn các bài báo dạng dài.
Draft.js đã được giới thiệu tại React.js Conf vào tháng 2 năm 2016.

Cài đặt

Để cài đặt Draft.js thì các bạn mạnh dạn thực hiện combo sau đây:

npm install draft-js
# hoặc dùng yarn
yarn add draft-js

Sử dụng

Về cách sử dụng thì đang có 2 hướng tiếp cận là theo class component hoặc function component. Trong bài viết này thì mình sẽ sử dụng cách function component nhé.
Đơn giản chỉ là vì mình lười chơi với this.abc, this.xyz, this.blabla, ... thôi ? Và đây là cú pháp cơ bản để sử dụng nó:

// file Editor.js
import React, { useRef, useState } from 'react';
import {Editor, EditorState} from 'draft-js'; function MyEditor() { const editorRef = useRef(); const [editorState, setEditorState] = useState( () => EditorState.createEmpty(), ); const focus = () => { editorRef.current.focus(); } return ( <div className="custom-editor" onClick={focus}> // hàm focus ở đây có tác dụng khi chúng ta click  <Editor // vào thẻ div chứa editor thì sẽ focus vào editor  ref={editorRef} // để chúng ta cào phím lun :) editorState={editorState} onChange={setEditorState} /> </div> );
} export default MyEditor;

Còn đây là code css mình chỉnh lại editor cho đẹp, ngoài ra các bạn có thể dùng thẳng css của draft.js bằng cách đưa thẳng file draft-js/dist/Draft.css của nó vào file js.

.custom-editor { padding: 24px 200px;
} .DraftEditor-root { min-height: 200px; border: solid 1px #bbb; box-sizing: border-box; border: 1px solid #ddd; cursor: text; padding: 16px; border-radius: 2px; margin-bottom: 2em; box-shadow: inset 0px 1px 8px -3px #ABABAB; background: #fefefe;
}

Và đây là kết quả nhé

Một vài tính năng cơ bản

RichUtils

RichUtils chứa thông tin về các lệnh chính có sẵn cho trình chỉnh sửa, chẳng hạn như Cmd + B (đậm), Cmd + I (nghiêng), ...
Các bước dùng nó sẽ như thế này:

  • import RichUtils
  • Khi click vào inline style button thì sử dụng hàm toggleInlineStyle(editorState, style) để tạo mới một editorState.
  • Cập nhật trạng thái editor mới nhất vào editorState.
// Ví dụ với bold style
import { RichUtils } from 'draft-js';
const onBoldClick = (e) { e.preventDefault(); // Mình dùng preventDefault() để giữ con trỏ chuột vẫn còn ở trong editor nhé các bạn setEditorState(RichUtils.toggleInlineStyle(editorState, 'BOLD'));
}
// ở file render Editor
return ( <div className="custom-editor" onClick={focus}> <span onMouseDown={onBoldClick}> Bold </span> <Editor ref={editorRef} editorState={editorState} onChange={setEditorState} /> </div>
);

Hàng demo nè

AtomicBlockUtils

AtomicBlockUtils là một tập hợp tĩnh các chức năng tiện ích để chỉnh sửa khối atomic. Khối atomic này có thể là image, audio hay thậm chí là video.
Trong mỗi trường hợp, các phương thức này chấp nhận các đối tượng EditorState với các tham số liên quan và trả về các đối tượng EditorState mới để cập nhật vào editor.
Ví dụ về chèn hình ảnh sử dụng AtomicBlockUtils.insertAtomicBlock:

// hàm addImage
const addImage = (e) => { e.preventDefault(); const contentState = editorState.getCurrentContent(); const contentStateWithEntity = contentState.createEntity( 'image', 'IMMUTABLE', {src: imgUrl} ); const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); const newEditorState = EditorState.set( editorState, {currentContent: contentStateWithEntity} ); setEditorState(AtomicBlockUtils.insertAtomicBlock( newEditorState, entityKey, ' ' )); }; // Tiến hành render Image component trong editor const Image = ({ contentState, block }) => { const entity = contentState.getEntity( block.getEntityAt(0) ); const { src } = entity.getData(); console.log(src) return <img src={src} />;
}; function mediaBlockRenderer(block) { if (block.getType() === 'atomic') { return { component: Image, editable: false, }; } return null;
} // editor component
<Editor blockRendererFn={mediaBlockRenderer} ...
/>

Các bước thực hiện sẽ là:

  • Lấy content hiện tại
  • Tạo mới một Entity với type là image và mutability sẽ là IMMUTABLE
  • Lấy entity key vừa tạo và dùng AtomicBlockUtils.insertAtomicBlock để update trạng thái mới của editor.
  • Thêm blockRendererFn vào trong Editor component để tiến hành render Image.

Và đây là kết quả demo thử của mình:
Như các bạn có thể thấy là rất i zì phải ko nào ?
Có một điểm trừ nhỏ là khi insert image bằng cách này thì sẽ xuất hiện 2 dòng trống ở trước và sau khi insert tạo cảm giác trống vắng cô đơn khi đêm về không gấu.
Hiện tại mình vẫn đang tìm cách xóa nó nên anh em nào biết solution có thể comment để mình test thử lun cho lẹ nhé ???

Decorators

Khái niệm decorator dựa trên việc quét nội dung của một ContentBlock nhất định để tìm phạm vi văn bản phù hợp với strategy đã xác định, sau đó hiển thị chúng bằng một React component được chỉ định.

Các bạn có thể sử dụng lớp CompositeDecorator để xác định hành vi decorator mong muốn của mình.
Sau đây là ví dụ tìm link ở trong editor của mình.

// Định nghĩa hàm tìm kiếm link entity
function findLinkEntities(contentBlock, callback, contentState) { contentBlock.findEntityRanges( (character) => { const entityKey = character.getEntity(); return ( entityKey !== null && contentState.getEntity(entityKey).getType() === 'LINK' ); }, callback );
}
// Định nghĩa Link component sẽ được dùng nếu tìm thấy link
const Link = ({ contentState, entityKey, children }) => { const { url } = contentState.getEntity(entityKey).getData(); return ( <a href={url}> {children} </a> );
};
// Khởi tạo decorator và thêm vào editorState
const decorator = new CompositeDecorator([ { strategy: findLinkEntities, component: Link, },
]);
const [editorState, setEditorState] = React.useState( () => EditorState.createEmpty(decorator), ); // Định nghĩa hàm addLink const addLink = (e) => { e.preventDefault(); const contentState = editorState.getCurrentContent(); const contentStateWithEntity = contentState.createEntity( 'LINK', 'MUTABLE', { url: imgUrl }, ); const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity }); setEditorState(RichUtils.toggleLink( newEditorState, newEditorState.getSelection(), entityKey )); };

And here is my demo

Tổng kết

Vậy là chúng ta đã cùng nhau tìm hiểu một vài thứ cơ bản của Draft Js rùi đấy.
Trên đây mình đã giới thiệu sơ lược về Draft Js thông qua các việc sau:

  • Cách cài đặt và sử dụng Draft js đơn giản.
  • Sử dụng RichUtils cho các inline style như: đậm nghiêng, gạch chân, ...
  • Sử dụng AtomicBlockUtils để chèn các khối atomic (image, audio, video) vào editor.
  • Sử dụng CompositeDecorator để tìm và custom một vài nội dung nào đó.

Hy vọng qua bài viết này các bạn có thể dần dần tiếp cận và làm chủ con package này ? Cùng nhau tìm hiểu để có thể khám phá thêm nhiều thứ hay ho mà mình chưa giới thiệu hết được trong bài viết ???

Bình luận

Bài viết tương tự

- vừa được xem lúc

2021, chúng ta cần tối ưu hóa việc tải hình ảnh trên web như nào?

Rất chào các bạn,. Như các bạn đã biết, trong kỉ nguyên công nghệ, song song với sự sinh ra dày đặc của các trang web mới cũng là sự biến mất của những trang web "lạc hậu" hay hoạt động kém hiệu quả.

0 0 57

- vừa được xem lúc

Cách mình "hack" được vào hẹ thống của SMAS để xem điểm.

Cách mà mình "hack" được vào hệ thống của SMAS. Thật ra dùng từ hack cũng không đúng lắm, chỉ là một vài trick để lừa hệ thống mà thôi.

0 0 146

- vừa được xem lúc

Giải thích một số JAVASCRIPT ARRAY METHOD với EMOJIS

Như chúng ta đã biết, Array trong JS có rất nhiều method tiện dụng có thể hỗ trợ chúng ta. Sau đây là một số method thông dụng được giải thích bằng các emoji. Thêm một hoặc nhiều phần tử vào sau mảng. livestock.

0 0 46

- vừa được xem lúc

Testing trong Javascript với Jest (Phần 1)

Hello 500 anh em, lại là mình đây. Chú bé coder yêu màu tím thích màu hồng và ghét sự giả dối đây .

0 0 270

- vừa được xem lúc

Áp dụng kiến trúc 3 Layer Architecture vào project NodeJS

The problem encountered. Các framework nodejs phổ biết như Express cho phép chúng ta dễ dàng tạo ra Resful API xử lí các request từ phía client một cách nhanh chóng và linh hoạt.

0 0 80

- vừa được xem lúc

Một số String methods cần biết trong javascript

String là một trong những phần quan trọng nhất trong javascript, ngoài những methods hay dùng như trim, concat, subString, toUpperCase, toLowerCase; Javascript còn cung cấp cho chúng ta rất nhiều methods hữu ích khác để thao tác và giải quyết các vấn đề dễ dàng hơn khi làm việc với String. Mặc dù nh

0 0 39