change position landscape or portrait in react - reactjs

i have a landscape and portrait image. how can i pop up it correctly ?
my pop up:
<Modal
visible={this.state.visible}
width="600"
height="400"
effect="fadeInUp"
onClickAway={() => this.closeModal()}
>
<div>
<img
src={"/img/" + this.state.LinkName}
width="600"
height="400"
alt="Houston We have pb"
/>
my idea, i will add "land" and "portrait" string to each image.
so i can test this.state.LinkName.contains("Land") then
width = 600 and height = 400
else width = 400 and height = 600
how can i do that in react ?
i am newbie in react technologie.
i modifie the code :
handleButtonClick = (e, row) => {
var bild = new Image();
bild.src = "/img/" + row.original.Cert;
//Image.getSize(bild.src, (width, height) => {this.setState({width, height})});
//const {width, height} = Image.resolveAssetSource(bild.src);
var Pos
bild.onload = function()
{
Pos = bild.width > bild.height ? "Land" : "Port";
}
this.setState({ visible: true, LinkName: row.original.Cert, ThePos: Pos });
};
Now i get the real width and height.
the problem now, the variable ThePos is always undifined.

You can just test on this.state.LinkName.contains("Land")
like this:
render(){
return(
<Modal
visible={this.state.visible}
width={this.state.LinkName.contains("Land")? "600" : "400"}
height={this.state.LinkName.contains("Land")? "400" : "600"}
effect="fadeInUp"
onClickAway={() => this.closeModal()}
>
<div>
<img
src={"/img/" + this.state.LinkName}
width={this.state.LinkName.contains("Land")? "600" : "400"}
height={this.state.LinkName.contains("Land")? "400" : "600"}
alt="Houston We have pb"
/>
</Modal>
)
}

i found it:
handleButtonClick = (e, row) => {
var bild = new Image();
bild.src = "/img/" + row.original.Cert;
bild.onload = () =>
this.setState({ visible: true, LinkName: row.original.Cert, ThePos: bild.width > bild.height ? "Land" : "Port" });
};

Related

React Guarantee that part of function runs after DOM updates

Currently I have a textarea like this:
<textarea
onChange={handleTextAreaChange}
ref={textAreaRef as MutableRefObject<HTMLTextAreaElement>}
id={id}
value={content}
></textarea>
I am implementing some buttons to add markdown to the textarea to make it easier for the user to update and have this function for bold:
const handleBoldClick = useCallback(() => {
const selectionStart = textAreaRef.current?.selectionStart;
const selectionEnd = textAreaRef.current?.selectionEnd;
if (selectionStart && selectionEnd) {
setContent(
prevContent =>
prevContent.substring(0, selectionStart) +
'**' +
prevContent.substring(selectionStart, selectionEnd) +
'**' +
prevContent.substring(selectionEnd, prevContent.length)
);
} else {
setContent(prevContent => prevContent + '****');
// Want this to run after textarea gets updated
textAreaRef.current?.focus();
textAreaRef.current?.setSelectionRange(
content.length - 3,
content.length - 3
);
}
const changeEvent = new Event('change', { bubbles: true });
// Want to run this after textarea is updated
textAreaRef.current?.dispatchEvent(changeEvent);
}, [content]);
setContent is the setter for content which is passed to the textarea. Is there a way to guarantee the parts I've marked with comments as wanting to only run once the DOM gets updated run when I want them to?
I finagled around with things and went with this approach (gonna post the entire component, which contains some stuff irrelevant to the question):
const MarkdownTextArea = ({
value,
onBlur = () => {},
onChange = () => {},
touched = false,
error,
id,
label
}: MarkdownTextAreaProps) => {
const [content, setContent] = useState(value ?? '');
const [numberOfRows, setNumberOfRows] = useState(5);
const [numberOfCols, setNumberOfCols] = useState(20);
const [isPreview, setIsPreview] = useState(false);
const [changed, setChanged] = useState<'bold' | null>();
const textAreaRef = useRef<HTMLTextAreaElement>();
useEffect(() => {
const setColsAndRows = () => {
const newColumnsNumber = Math.floor(
(textAreaRef.current?.offsetWidth ?? 100) /
(convertRemToPixels(1.2) / 1.85)
);
setNumberOfCols(newColumnsNumber);
setNumberOfRows(calculateNumberOfRows(content, newColumnsNumber));
};
setColsAndRows();
window.addEventListener('resize', setColsAndRows);
return () => {
window.removeEventListener('resize', setColsAndRows);
};
}, [content]);
const handleTextAreaChange: ChangeEventHandler<HTMLTextAreaElement> =
useCallback(
event => {
onChange(event);
setContent(event.target.value);
setNumberOfRows(
calculateNumberOfRows(
event.target.value,
textAreaRef.current?.cols ?? 20
)
);
},
[onChange]
);
const handleBoldClick = useCallback(() => {
const selectionStart = textAreaRef.current?.selectionStart;
const selectionEnd = textAreaRef.current?.selectionEnd;
if (selectionStart && selectionEnd) {
setContent(
prevContent =>
prevContent.substring(0, selectionStart) +
'**' +
prevContent.substring(selectionStart, selectionEnd) +
'**' +
prevContent.substring(selectionEnd, prevContent.length)
);
} else {
setContent(prevContent => prevContent + '****');
}
setChanged('bold');
}, []);
if (changed && textAreaRef.current?.value === content) {
const changeEvent = new Event('change', { bubbles: true });
textAreaRef.current?.dispatchEvent(changeEvent);
if (changed === 'bold' && textAreaRef.current) {
textAreaRef.current.focus();
textAreaRef.current.selectionStart = content.length - 2;
textAreaRef.current.selectionEnd = content.length - 2;
}
setChanged(null);
}
return (
<div className={classes.container} data-testid="markdown-text-area">
<div className={classes['header']}>
<label className={classes.label} htmlFor={id}>
{label}
</label>
<Button
positive
style={{ justifySelf: 'flex-end' }}
onClick={() => setIsPreview(prev => !prev)}
>
{isPreview ? 'Edit' : 'Preview'}
</Button>
</div>
<div className={classes['text-effect-buttons']}>
<button
className={classes['text-effect-button']}
onClick={handleBoldClick}
type="button"
style={{ fontWeight: 'bold' }}
>
B
</button>
</div>
{isPreview ? (
<div className={classes['markdown-container']} id={id}>
<MarkdownParser input={content} />
</div>
) : (
<textarea
onChange={handleTextAreaChange}
className={`${classes['text-input']}${
error && touched ? ` ${classes.error}` : ''
}`}
ref={textAreaRef as MutableRefObject<HTMLTextAreaElement>}
rows={numberOfRows}
cols={numberOfCols}
onBlur={onBlur}
id={id}
value={content}
></textarea>
)}
{error && touched && (
<div className={classes['error-message']}>{error}</div>
)}
</div>
);
};
The part of the following component most relevant to answering the question is the following:
if (changed && textAreaRef.current?.value === content) {
const changeEvent = new Event('change', { bubbles: true });
textAreaRef.current?.dispatchEvent(changeEvent);
if (changed === 'bold' && textAreaRef.current) {
textAreaRef.current.focus();
textAreaRef.current.selectionStart = content.length - 2;
textAreaRef.current.selectionEnd = content.length - 2;
}
setChanged(null);
}

Dont working prev and next methods at flicking slider

i have two buttons, but thay are not move slider. maybee anyone have an example?
<button className="carousel__back-button" onClick={() => flicking.current.prev()}> </button>
<button className="carousel__next-button" onClick={() => flicking.current.next()}> </button>
----------initial flicking options----------------------------------------------------------------------
let flicking: React.RefObject<Flicking> =
React.createRef<Flicking>()
let oprionsFlicking = {
className: "flicking",
ref: flicking,
hanger: "0",
anchor: "0",
bound: true,
gap: 5
}
-----------use here-----------------------------------------------------------------------------------------
<StyledDateWrapper>
<Flicking ref={flicking} {...oprionsFlicking} style={{height: '51px', width: '100%'}}>
{ currentMonthDates.length > 0 && Object.entries(currentMonthDates
.reduce((a, i) => {
const normolizeDate = i * 1000;
const dayNumber = new Date(normolizeDate).getDate()
const dayWeek = new Date(normolizeDate).getDay()
return {...a, [dayNumber]: { dayWeek, checked: false }}
}, {}))
.map((el: any) => el[0] == checkedDay ? ([...el, el[1].checked = true]) : el)
.map(([date, day]: any) => {
const monthIdx = new Date(currentMonth.start ? currentMonth.start * 1000 : new Date()).getMonth();
const month = monthesMapper[monthIdx]
return <StyledDateTabWrapper key={date} className="panel">
<DateTab
onClick={() => takeDay(date)}
selected={day.checked}
date={`${date} ${month}`}
day={`${daysInWeek[day.dayWeek - 1] ? daysInWeek[day.dayWeek - 1] : 'Вс'}`}
holiday={daysInWeek[day.dayWeek - 1] ? false : true}
theme={PrimaryThemeConfig}/>
</StyledDateTabWrapper>
}) }
</Flicking>
{ renderArrows() }
</StyledDateWrapper>
p.s. react functional component

Why my setInterval() is speeding up inside my React image slider?

Why my interval is speeding up?
When I press any of my buttons NextImage() or PrevImage() my interval starts speeding up and the image starts glitching. Any advice or help?
Here's my code =>
//Image is displayed
const [image, setImage] = React.useState(1);
let imageShowed;
if (image === 1) {
imageShowed = image1;
} else if (image === 2) {
imageShowed = image2;
} else if (image === 3) {
imageShowed = image3;
} else {
imageShowed = image4;
}
// Auto change slide interval
let interval = setInterval(
() => (image === 4 ? setImage(1) : setImage(image + 1)),
5000
);
setTimeout(() => {
clearInterval(interval);
}, 5000);
// Change image functionality
const ChangeImage = (index) => {
setImage(index);
};
/ /Next image
const NextImage = () => {
image === 4 ? setImage(1) : setImage(image + 1);
};
// Previous image
const PrevImage = () => {
image === 1 ? setImage(4) : setImage(image - 1);
};
When you need to have some logic which is depend on changing a variable, it's better to keep those logic inside useEffect
const interval = useRef(null);
const timeout = useRef(null);
useEffect(() => {
interval.current = setInterval(
() => (image === 4 ? setImage(1) : setImage((i) => i + 1)),
5000
);
timeout.current = setTimeout(() => {
clearInterval(interval.current);
}, 5000);
return () => {
clearInterval(interval.current);
clearTimeout(timeout.current);
}
}, [image]);
one point to remember is that if you use a variable instead of using useRef it can increase the possibility of clearing the wrong instance of interval or timeout during the rerenders. useRef can keep the instance and avoid any unwanted bugs
Your approach causes so many problems and you should learn more about react (watch youtube tutorials about react), I did make a working example slider hope to help you and people in the future:
let interval;
const images = [
"https://picsum.photos/300/200?random=1",
"https://picsum.photos/300/200?random=2",
"https://picsum.photos/300/200?random=3",
"https://picsum.photos/300/200?random=4",
"https://picsum.photos/300/200?random=5",
];
const App = () => {
const [slide, setSlide] = React.useState(0);
React.useEffect(() => {
interval = setInterval(() => {
NextSlide();
clearInterval(interval);
}, 5000);
return () => {
clearInterval(interval);
};
}, [slide]);
const ChangeSlideDots = (index) => {
setSlide(index);
};
const NextSlide = () =>
setSlide((prev) => (slide === images.length - 1 ? 0 : prev + 1));
const PrevSlide = () =>
setSlide((prev) => (slide === 0 ? images.length - 1 : prev - 1));
return (
<div style={styles.root}>
<img style={styles.imageDiv} src={images[slide]} />
<button style={styles.buttons} onClick={PrevSlide}>
◁
</button>
<div style={styles.dotDiv}>
{images.map((_, i) => (
<div
key={i}
style={i === slide ? styles.redDot : styles.blackDot}
onClick={() => ChangeSlideDots(i)}
>
.
</div>
))}
</div>
<button style={styles.buttons} onClick={NextSlide}>
▷
</button>
</div>
);
}
const styles = {
root: {
display: "flex",
position: "relative",
width: 300,
height: 200,
},
buttons: {
backgroundColor: "rgb(255 255 255 / 37%)",
border: "none",
zIndex: 2,
flex: 1,
},
imageDiv: {
position: "absolute",
zIndex: 1,
width: 300,
height: 200,
},
dotDiv: {
flex: 10,
zIndex: 2,
fontSize: "30px",
display: "flex",
justifyContent: "center",
},
redDot: {
cursor: "pointer",
color: "red",
},
blackDot: {
cursor: "pointer",
color: "black",
},
};
ReactDOM.render(<App />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Anytime that you rerender your component, you will run the whole function once. So you will set an interval every time you use setImage(). In order to prevent this, you have to use side effect functions. here you should use useEffect() because you have a functional component. in order to make useEffect() only run once, you have to pass an empty array for dependecy array; So your useEffect will act like componentDidMount() in class components. try the code below:
let interval = null
useEffect(() => {
interval = setInterval(
() => (image === 4 ? setImage(1) : setImage(image + 1)),
5000
)
setTimeout(() => {
clearInterval(interval);
}, 5000)
}, [])
Thanks, everybody for your great answers appreciated a lot your time and help!
So, my final solution looks like this:
const images = [image1, image2, image3, image4];
const quotes = [
'Jobs fill your pockets, adventures fill your soul',
'Travel is the only thing you buy that makes you richer',
'Work, Travel, Save, Repeat',
'Once a year, go someplace you’ve never been before',
];
const App = () => {
//Image is displayed
const [image, setImage] = React.useState(0);
// Auto change slide interval
useEffect(() => {
return () => {
clearInterval(
setInterval((interval) => {
image === 3 ? setImage(1) : setImage(image + 1);
clearInterval(interval.current);
}, 5000)
);
};
}, [image]);
// Change image functionality
const ChangeImage = (index) => {
setImage(index);
};
//Next image
const NextImage = () => {
image === 3 ? setImage(1) : setImage(image + 1);
};
// Previous image
const PrevImage = () => {
image === 1 ? setImage(3) : setImage(image - 1);
};
return (
<Section>
<div className='slideshow-container'>
<div>
<img className='slider_image' src={images[image]} alt='slider' />
<h1 className='slider_title'>{quotes[image]}</h1>
</div>
<button className='slider_prev' onClick={PrevImage}>
❮
</button>
<button className='slider_next' onClick={NextImage}>
❯
</button>
</div>
<div>
<div>
{images.map((image, i) => (
<img
key={i}
alt={`slider${i}`}
src={image}
className='bottom_image'
onClick={() => ChangeImage(i)}
></img>
))}
</div>
</div>
</Section>
);
};

Moving an object on array makes konva objects lose their transform changes like rotation, position

I'm using react-konva, in it I have an array with all my konva objects, and I have a function that brings the selected object to the front or back of this array to create layers on my kanvas.
When I fire this function, the order of objects inside the array works fine but the transform changes on all objects are lost, what can I do to don't lose this params during this change on the array?
changeOrderArray = (front) => {
let { arrayObjectsLayer, selectedObject } = this.state;
front ?
arrayObjectsLayer.push(
arrayObjectsLayer.splice(
arrayObjectsLayer.findIndex(
elt => elt.id === selectedObject.id),
1)[0]
)
: arrayObjectsLayer.unshift(
arrayObjectsLayer.splice(
arrayObjectsLayer.findIndex(
elt => elt.id === selectedObject.id),
1)[0]
)
this.setState({
arrayObjectsLayer
});
}
the loop
arrayObjectsLayer.map((item, index) => {
return (
item.type === 'square' ?
<RectanguleComponent
shapeProps={item}
isSelected={
selectedObject && item.id === selectedObject.id
}
onSelect={() => {
this.selectShape(item);
}}
onChange={newAttrs => {
const item = arrayObjectsLayer.slice();
item[index] = newAttrs;
this.setArrayObject(item);
}}
/>
:
item.type === 'triangule' ?
<TrianguleComponent
shapeProps={item}
isSelected={
selectedObject && item.id === selectedObject.id
}
onSelect={() => {
this.selectShape(item);
}}
onChange={newAttrs => {
const item = arrayObjectsLayer.slice();
item[index] = newAttrs;
this.setArrayObject(item);
}}
/>
:
etc ...
basic component with transformer
export const RectanguleComponent = ({ shapeProps, isSelected, onSelect, onChange }) => {
const shapeRef = React.useRef();
const trRef = React.useRef();
React.useEffect(() => {
if (isSelected) {
trRef.current.setNode(shapeRef.current);
trRef.current.getLayer().batchDraw();
}
}, [isSelected]);
return (
<React.Fragment>
<Rect
onClick={onSelect}
ref={shapeRef}
{...shapeProps}
draggable
onDragEnd={e => {
onChange({
...shapeProps,
x: e.target.x(),
y: e.target.y()
});
}}
onTransformEnd={e => {
// transformer is changing scale
const node = shapeRef.current;
const scaleX = node.scaleX();
const scaleY = node.scaleY();
// we will reset it back
node.scaleX(1);
node.scaleY(1);
onChange({
...shapeProps,
x: node.x(),
y: node.y(),
width: node.width() * scaleX,
height: node.height() * scaleY
});
}}
/>
{isSelected && <Transformer ref={trRef} />}
</React.Fragment>
);
};
You need to save the rotation on onTransformEnd
onTransformEnd={(e) => {
const node = imageRef.current;
const scaleX = node.scaleX();
const scaleY = node.scaleY();
// we will reset it back
node.scaleX(1);
node.scaleY(1);
onChange({
...shapeProps,
rotation: node.rotation(),
x: node.x(),
y: node.y(),
width: node.width() * scaleX,
height: node.height() * scaleY
});
}}
GitHub solution reference.

onDblClick in react-konva not working

I'm using react-konva to render canvas. I assign onDblClick for a Group, I also assign onClick, onDragEnd to this Group. In onDragEnd handler, I have an axios POST request to server. Whenever I double click the Group, the onDragEnd event is fired. Does anybody know what is the problem here ?
Here are my code
handleMoving(){
var thisElement = this.refs[this.state.key];
this.setState({
x: thisElement.x(),
y: thisElement.y(),
width: thisElement.getWidth(),
height: thisElement.getHeight()
});
this.focus();
}
handleDoubleClick(){
console.log('dbclick');
this.focus();
const stage = this.refs[this.state.key+'_wrapper'].getParent().getParent().getParent();
const pos = this.refs[this.state.key].getAbsolutePosition();
document.getElementById('newText').addEventListener('keydown',this.handleTextChange);
document.getElementById('newTextWrapper').style.position = "absolute";
document.getElementById('newTextWrapper').style.left = pos.x+"px";
document.getElementById('newTextWrapper').style.top = pos.y+20+"px";
document.getElementById('newText').style.width = this.refs[this.state.key+'_wrapper'].getWidth()+"px";
document.getElementById('newTextWrapper').style.display = 'block';
}
syncToServer(){
axios.post('/api/element/text/update',{
uid:this.state.uid,
key:this.state.key,
content:this.state.content,
stage:{
x:this.state.x + this.refs[this.state.key].getParent().x(),
y:this.state.y + this.refs[this.state.key].getParent().y(),
width:this.state.width,
height:this.state.height,
fontSize:this.state.fontSize
}
}).then(function(response){
console.log(response.data);
});
}
render(){
return (
<Layer>
<Group onDblClick = {this.handleDoubleClick}
onClick = {this.handleClick}
onDragMove = {this.handleMoving}
onDragEnd = {this.syncToServer}
draggable = "true">
<Rect ref = {this.state.key + '_wrapper'}
x = {this.state.x}
y = {this.state.y}
width = {this.state.width}
height = {this.state.height}
visible = {false}
fill = 'lightgrey'
cornerRadius = {3}>
</Rect>
<Text ref = {this.state.key}
x = {this.state.x}
y = {this.state.y}
fontSize = {this.state.fontSize}
fontFamily = {this.state.fontFamily}
text = {this.state.content}
fill = {this.state.color}
padding = {20}
>
</Text>
</Group>
</Layer>
);
}
Try to use the node of the ref.
node.on('dblclick dbltap', () => {
console.log('you clicked me!');
});

Resources