[21/07/15] 에디터 리팩토링 & 액션 리팩토링
에디터 모달 리팩토링
기존의 config 옵션 변수
const modules = {
toolbar: {
container: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
[{ list: 'ordered' }, { list: 'bullet' }],
['link', 'image'],
[{ align: [] }, { color: [] }, { background: [] }],
['clean'],
],
},
};
const formats = [
'header',
'font',
'size',
'bold',
'italic',
'underline',
'list',
'bullet',
'align',
'color',
'background',
'image',
];
변경된 config 옵션 변수
const config = {
modules: {
toolbar: {
container: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
[{ list: 'ordered' }, { list: 'bullet' }],
['link', 'image'],
[{ align: [] }, { color: [] }, { background: [] }],
['clean'],
],
handlers: { image: onClickImage },
},
},
formats: [
'header',
'font',
'size',
'bold',
'italic',
'underline',
'list',
'bullet',
'align',
'color',
'background',
'image',
],
};
사용
<ReactQuill
{...config}
ref={editorRef}
value={value}
onChange={str => onChange(str)}
readOnly={readOnly}
/>
const modules = {
toolbar: {
container: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
[{ list: 'ordered' }, { list: 'bullet' }],
['link', 'image'],
[{ align: [] }, { color: [] }, { background: [] }],
['clean'],
],
},
};
const formats = [
'header',
'font',
'size',
'bold',
'italic',
'underline',
'list',
'bullet',
'align',
'color',
'background',
'image',
];
const config = {
modules: {
toolbar: {
container: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
[{ list: 'ordered' }, { list: 'bullet' }],
['link', 'image'],
[{ align: [] }, { color: [] }, { background: [] }],
['clean'],
],
handlers: { image: onClickImage },
},
},
formats: [
'header',
'font',
'size',
'bold',
'italic',
'underline',
'list',
'bullet',
'align',
'color',
'background',
'image',
],
};
<ReactQuill
{...config}
ref={editorRef}
value={value}
onChange={str => onChange(str)}
readOnly={readOnly}
/>
굳이 두개로 나누기보단 깔끔하게 하나의 config
변수로 관리하여 ReactQuill
에게 전달해준다.
key & value
const [key, setKey] = useState<KeyEngType>('introduce');
const [value, setValue] = useState<string>(currentProduct[key]);
기존의 value
상태는 introduce
guide
두개를 합쳐 관리하였음.
const [type, setType] = useState<'introduce' | 'guide'>('introduce');
const [introduce, setIntroduce] = useState<string>(currentProduct.introduce);
const [guide, setGuide] = useState<string>(currentProduct.guide);
value
하나로 두 개의 키 데이터 관리하기보단, 각각 따로 나두는 것이 키값이 변경되어도 데이터를 보존하는데 좋을 것이라 판단됨.
public async imageHandler() {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = async () => {
var file: any = input.files[0];
var formData = new FormData();
formData.append('image', file);
var fileName = file.name;
const res = await this.uploadFiles(file, fileName, quillObj);
};
}
위는 이곳을 참고한 ReactQuill
에서의 이미지 업로드 코드이다. 살펴보면 이미지 핸들러가 실행될때마다 input
을 만들어 로직이 수행되는 것을 알 수 있다.
위와 같이 하기 보단 인풋태그를 만들어 두고 ReactQuill
의 imageHandler
가 실행되면 인풋태그를 클릭하는 로직으로 변경하는게 효율적
const Editor: React.FC<IEditorProps> = ({ value, onChange, readOnly = false }) => {
const editorRef = useRef<ReactQuill>(null);
// 이미지 탐색기(파인더) 띄우기
const imageRef = useRef<HTMLInputElement>(null);
const onClickImage = useCallback(() => {
if (!imageRef.current) return;
imageRef.current.click();
}, []);
// 이미지 업로드
const onChangeFile = async (e: ChangeEvent<HTMLInputElement>) => {
const { files } = e.target;
if (files) {
const formData = new FormData();
formData.append('img', files[0]);
try {
const {
data: { url },
} = await uploadImage(formData);
if (editorRef.current) {
const editor = editorRef.current.getEditor();
const range = editor.getSelection();
if (url && range) {
editor.insertEmbed(range.index, 'image', BACKEND_URL + url);
}
}
} catch (error) {
console.error(error);
}
}
};
...
return (
<Wrapper>
<ReactQuill
{...config}
ref={editorRef}
value={value}
onChange={str => onChange(str)}
readOnly={readOnly}
/>
{!readOnly && (
<input
ref={imageRef}
type="file"
accept="image/png, image/jpeg"
alt="이미지 업로드"
onChange={onChangeFile}
hidden
/>
)}
</Wrapper>
);
};
기존 액션 수정하기
상태의 불변성을 유지하지 못하고 있는 액션을 리팩토링하자.
const onClickSelect = (hashtag: any) => {
const newHashtag = { hashtagId: hashtag.id, hashtagName: hashtag.name };
const tempCurrentProduct = currentProduct;
tempCurrentProduct[columnString] = [...tempCurrentProduct[columnString], newHashtag];
dispatch({
type: 'CHANGE_PRODUCT_CELL',
payload: { newValue: tempCurrentProduct },
});
};
위 코드는 다음 그림과 같다. 얕은 복사가 수행되고 있음.
따라서 불변성이 유지되지 못하므로 위험한 코드임. 다음 액션으로 변경한다.
//CHANGE_PRODUCT_CELL => CHANGE_PRODUCT_INFO
case 'CHANGE_PRODUCT_INFO':
return {
...state,
table: {
...state.table,
currentProducts: {
...state.table.currentProducts,
[state.modal.currentProduct.id]: {
...state.modal.currentProduct,
[action.payload.key]: action.payload.value,
},
},
isModifed: { ...state.table.isModifed, [state.modal.currentProduct.id]: true },
},
modal: {
...state.modal,
currentProduct: {
...state.modal.currentProduct,
[action.payload.key]: action.payload.value,
},
},
};
key
와 value
를 받아 product
의 key
속성 값을 value
로 변경해준다.
const onClickSelect = (hashtag: any) => {
dispatch({
type: 'CHANGE_PRODUCT_INFO',
payload: {
key: columnString,
value: [
...currentProduct[columnString],
{ hashtagId: hashtag.id, hashtagName: hashtag.name },
],
},
});
};
클립보드 이미지 복사하기(React-Quill)
ReactQuill에서 onPaste 이벤트를 지원할 줄 알았는데, 아니였나보다. 복붙을 하면 그 정보가 이미지인지 아닌지를 점검한 후, 이미지라면 서버에 올리는 작업 수행하면 될 것 같다.
아직 할게 많아서 4순위로 할 예정이다.
랜딩페이지 업체탐색 소개 기능 추가
다음 사진은 우리 디자인 사진이다.
여기서 css 궁금한 거, 다음 두개의 영역을 div
로 나누어 스타일링 하는 방법말고
다음 코드 구조에서 위 디자인과 같은 스타일을 낼 수는 없을까?
이는 다음시간에..
Ref
- codekata
- hexo
Author And Source
이 문제에 관하여([21/07/15] 에디터 리팩토링 & 액션 리팩토링), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@rat8397/210715-에디터-리팩토링-액션-리팩토링
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Author And Source
이 문제에 관하여([21/07/15] 에디터 리팩토링 & 액션 리팩토링), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@rat8397/210715-에디터-리팩토링-액션-리팩토링저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)