react-ace + flexlayout-react: Ace editor keeps resetting - reactjs

I have a FlexLayout (from flexlayout-react) which contains an AceEditor (from react-ace). For testing I added a Test component as well.
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import FlexLayout from 'flexlayout-react';
import AceEditor from 'react-ace';
// display an Ace editor (here with fixed size)
const Editor = () => {
return (
<AceEditor
width="200px"
height="200px"
value="foo"
/>
);
}
// an increment button, just something simple stateful
const Test = () => {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
{count}
</button>
</div>
);
};
// two columns for editor and test component
const model = FlexLayout.Model.fromJson({
global: {},
borders: [],
layout: {
type: 'row',
weight: 50,
children: [
{
type: 'tabset',
weight: 50,
selected: 0,
children: [
{
type: 'tab',
name: 'A',
component: 'editor',
},
],
},
{
type: 'tabset',
weight: 50,
selected: 0,
children: [
{
type: 'tab',
name: 'B',
component: 'test',
},
],
},
],
},
});
const factory = node => {
switch (node.getComponent()) {
case 'editor': {
return <Editor />;
}
case 'test': {
return <Test />;
}
default:
return null;
}
}
// display the flex layout
const Ide = () => {
return (
<FlexLayout.Layout
model={model}
factory={factory}
/>
);
};
// render everything
ReactDOM.render(
<Ide />,
document.getElementById('react-container')
);
So what's going on?
Whenever the FlexLayout state changes (focus changed, dragging the divider, changing width), the text of the Ace editor is reset to foo. In contrast, the value of Test is preserved. Without the FlexLayout, the problem goes away.
So there seems to be a curious interaction between the two components, but I'm too inexperienced with React to figure it out. How would I go about debugging this? What are common avenues of approach with such an issue? Or any concrete ideas where the error is in this specific situation?

Related

Converting DraftJS class component to Functional component

The following Draftjs code is in class component. The plugins like CreateImage, Focus Plugin, and BlockDndPlugin are being imported from the DraftJS. I would be grateful if somebody can convert the class-based react components into Functional based react components...............................................................................................................................................................................................................................................................................................
import React, { Component } from 'react';
import { convertFromRaw, EditorState } from 'draft-js';
import Editor, { composeDecorators } from '#draft-js-plugins/editor';
import createImagePlugin from '#draft-js-plugins/image';
import createFocusPlugin from '#draft-js-plugins/focus';
import createBlockDndPlugin from '#draft-js-plugins/drag-n-drop';
import editorStyles from './editorStyles.module.css';
const focusPlugin = createFocusPlugin();
const blockDndPlugin = createBlockDndPlugin();
const decorator = composeDecorators(
focusPlugin.decorator,
blockDndPlugin.decorator
);
const imagePlugin = createImagePlugin({ decorator });
const plugins = [blockDndPlugin, focusPlugin, imagePlugin];
/* eslint-disable */
const initialState = {
entityMap: {
0: {
type: 'IMAGE',
mutability: 'IMMUTABLE',
data: {
src: '/images/canada-landscape-small.jpg',
},
},
},
blocks: [
{
key: '9gm3s',
text:
'You can have images in your text field which are draggable. Hover over the image press down your mouse button and drag it to another position inside the editor.',
type: 'unstyled',
depth: 0,
inlineStyleRanges: [],
entityRanges: [],
data: {},
},
{
key: 'ov7r',
text: ' ',
type: 'atomic',
depth: 0,
inlineStyleRanges: [],
entityRanges: [
{
offset: 0,
length: 1,
key: 0,
},
],
data: {},
},
{
key: 'e23a8',
text:
'You can checkout the alignment tool plugin documentation to see how to build a compatible block plugin …',
type: 'unstyled',
depth: 0,
inlineStyleRanges: [],
entityRanges: [],
data: {},
},
],
};
/* eslint-enable */
export default class CustomImageEditor extends Component {
state = {
editorState: EditorState.createWithContent(convertFromRaw(initialState)),
};
onChange = (editorState) => {
this.setState({
editorState,
});
};
focus = () => {
this.editor.focus();
};
render() {
return (
<div>
<div className={editorStyles.editor} onClick={this.focus}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={plugins}
ref={(element) => {
this.editor = element;
}}
/>
</div>
</div>
);
}
}
You can use useState and useRef hooks in FC & Modify your component accordingly..
const CustomImageEditor = () => {
const [editorState, setEditorState] = useState(
EditorState.createWithContent(convertFromRaw(initialState))
);
const editor = useRef();
const onChange = (editorStates) => {
setEditorState(editorStates);
};
const focus = () => {
editor.current.focus();
};
return (
<div>
<div className={editorStyles.editor} onClick={focus}>
<Editor
editorState={editorState}
onChange={onChange}
plugins={plugins}
ref={editor}
/>
</div>
</div>
);
};

Could not see the effect of changes of a state in react on the console before and after the event is handled

I don't know whether the question has been previously asked or not. I was playing around with states in react. My aim was to get a specific property of a data and based on that property I need to make changes on the appearance of a square on the screen, which I am getting upon and event call my desired task is being flawlessly being achieved but, when I am console logging the the state variable before and after the event the values are the same? I cannot see any changes in them. I have added screenshot, code and data format. I can't figure the reason.
Note: The event is being handled without any flaw the screen is changing as per my requirement
Parent Component:
import React from "react";
import boxes from "../data/boxes";
import Box from "./Box";
export default function Boxes() {
const [square, setSquare] = React.useState(boxes);
const squareBoxes = square.map((box) => {
return <Box key={box.id} on={box.on} toggle={toggle} id={box.id} />;
});
function toggle(id) {
console.log("Before Clicking:", square[id].on);
setSquare((prevSquare) => {
const newSquare = [];
for (let i = 0; i < prevSquare.length; i++) {
if (prevSquare[i].id === id) {
newSquare.push({
...prevSquare[i],
on: !prevSquare[i].on,
});
} else {
newSquare.push(prevSquare[i]);
}
}
return newSquare;
});
console.log("After Clicking:", square[id].on);
}
return (
<div className="box__main--container">
<div className="box__inner-container">
{squareBoxes}
</div>
</div>
);
}
Child Component:
import React from "react";
export default function Box(props) {
const styles = {
backgroundColor: props.on ? "#222222" : "transparent",
};
return (
<div
className="box"
style={styles}
onClick={() => props.toggle(props.id)}
></div>
);
}
Data Format:
export default [
{
id: 1,
on: false,
},
{
id: 2,
on: true,
},
{
id: 3,
on: false,
},
{
id: 4,
on: true,
},
{
id: 5,
on: false,
},
{
id: 6,
on: true,
},
];
Console Screenshot:
Image from the console

React Button Multi-Select, strange style behaviour

I am trying to create a simple button multi-select in React but I'm currently getting unexpected behaviour. I'd like users to be able to toggle multiple buttons and have them colourise accordingly, however the buttons seem to act a bit randomly.
I have the following class
export default function App() {
const [value, setValue] = useState([]);
const [listButtons, setListButtons] = useState([]);
const BUTTONS = [
{ id: 123, title: 'button1' },
{ id: 456, title: 'button2' },
{ id: 789, title: 'button3' },
];
const handleButton = (button) => {
if (value.includes(button)) {
setValue(value.filter((el) => el !== button));
} else {
let tmp = value;
tmp.push(button);
setValue(tmp);
}
console.log(value);
};
const buttonList = () => {
setListButtons(
BUTTONS.map((bt) => (
<button
key={bt.id}
onClick={() => handleButton(bt.id)}
className={value.includes(bt.id) ? 'buttonPressed' : 'button'}
>
{bt.title}
</button>
))
);
};
useEffect(() => {
buttonList();
}, [value]);
return (
<div>
<h1>Hello StackBlitz!</h1>
<div>{listButtons}</div>
</div>
);
}
If you select all 3 buttons then select 1 more button the css will change.
I am trying to use these as buttons as toggle switches.
I have an example running #
Stackblitz
Any help is much appreciated.
Thanks
I think that what you want to achieve is way simpler:
You just need to store the current ID of the selected button.
Never store an array of JSX elements inside a state. It is not how react works. Decouple, only store the info. React component is always a consequence of a pattern / data, never a source.
You only need to store the necessary information, aka the button id.
Information that doesn't belong to the state of the component should be moved outside. In this case, BUTTONS shouldn't be inside your <App>.
Working code:
import React, { useState } from 'react';
import './style.css';
const BUTTONS = [
{ id: 123, title: 'button1', selected: false },
{ id: 456, title: 'button2', selected: false },
{ id: 789, title: 'button3', selected: false },
];
export default function App() {
const [buttons, setButtons] = useState(BUTTONS);
const handleButton = (buttonId) => {
const newButtons = buttons.map((btn) => {
if (btn.id !== buttonId) return btn;
btn.selected = !btn.selected;
return btn;
});
setButtons(newButtons);
};
return (
<div>
<h1>Hello StackBlitz!</h1>
<div>
{buttons.map((bt) => (
<button
key={bt.id}
onClick={() => handleButton(bt.id)}
className={bt.selected ? 'buttonPressed' : 'button'}
>
{bt.title}
</button>
))}
</div>
</div>
);
}
I hope it helps.
Edit: the BUTTONS array was modified to add a selected property. Now several buttons can be selected at the same time.

React video component keeps re-rendering on browser tab switch even if using useCallBack function

Not sure if something is wrong with this Library or not. I have a video player component. I am using Plyr video player to style and display movies from YouTube and Vimeo.
I am facing a weird issue where, whenever I switch my browser tab.. the video player re-renders and loads again and again.
I am using useCallBack to only re-run if video ID or video Platform has been changed.
import { useCallback, useEffect, useState } from "react";
import getYouTubeId from "get-youtube-id";
import Plyr from "plyr-react";
import "plyr-react/dist/plyr.css";
interface videoInterface {
value: {
href: string;
videoPlatform: string;
};
}
export const video = ({ value: { href, videoPlatform } }: videoInterface) => {
const [videoID, setVideoID] = useState();
const loadVideo = useCallback(() => {
const id = getYouTubeId(href);
setVideoID(id);
}, [videoID, videoPlatform]);
useEffect(() => {
loadVideo();
}, [href]);
return (
videoID && (
<div className="relative my-6">
<div className="overflow-hidden rounded-md">
<Plyr
source={{
type: "video",
sources: [
{
src: videoID,
provider: videoPlatform,
},
],
}}
options={{
controls: [
"play-large",
"play",
"progress",
"current-time",
"mute",
"volume",
"fullscreen",
],
}}
/>
</div>
</div>
)
);
};

Draft.js comes with error: stack frames were collapsed

This is a question on how to deal with pasted image using draftjs in the case of Japanese input mode.
Recently I am using Draft.js as text editor. Sometimes we need to insert image into the editor like copy image and paste the image into the editor area.
Everything goes well but the strange thing happens when I try to input Japanese words together with pasted image.
As the image above, After inputing Japanese words, I pressed "Enter" key to confirm the Japanese words.
But after I pressed "Enter" Key, the following Errors happens.
I think the problem is cased by the Japanese Input dialog destroyed the entity key of Draft.js
Could someone give me some hint on how to solve the problem?
My code is the following.
import React, {useState} from 'react';
import './App.css';
import {Editor, EditorState, convertFromRaw, ContentBlock} from 'draft-js';
function App() {
const initData = convertFromRaw({
blocks: [
{
key: "98pea",
text: "https://dummyimage.com/100x100/000/fff",
type: "atomic",
depth: 0,
inlineStyleRanges: [],
entityRanges: [
{
offset: 0,
length: 1,
key: 0,
},
],
data: {},
},
{
key: "16d04",
text: "testtest。",
type: "unstyled",
depth: 0,
inlineStyleRanges: [],
entityRanges: [],
data: {},
},
],
entityMap: {
0: {
type: "image",
mutability: "IMMUTABLE",
data: { src: "https://dummyimage.com/100x100/000/fff" },
},
},
});
const initState = EditorState.createWithContent(initData)
const [editorState,setEditorState] = useState(initState)
const Image = (props:any) => {
return <img src={props.src} alt="" />;
};
const Media = (props:any) => {
const entity = props.contentState.getEntity(props.block.getEntityAt(0));
const { src } = entity.getData();
const type = entity.getType();
let media;
if (type === "image") {
media = <Image src={src} />;
}
return media;
};
const myBlockRenderer = (block:ContentBlock) => {
if (block.getType() === "atomic") {
return {
component: Media,
editable: false,
};
}
return null
}
return (
<div className="App">
<div>
<Editor editorState={editorState} onChange={setEditorState} blockRendererFn={myBlockRenderer} />
</div>
</div>
);
}
export default App;

Resources