How add width div with React? - reactjs

Add a div and a button. When you click - increase the width of the div by 3 px. Add a Reset button that allows you to reset the state to the initial width.
const three = (e) => {
let value = e.target.value;
this.setState({ three: value });
}

We need to define a state variable to toggle and based on its status we can toggle width as well, so our code would be something like this -:
Working demo -> https://stackblitz.com/edit/react-576vo5?file=index.js
In js file -:
render() {
return (
<div>
<div className={`flexible-width ${this.state.increaseWidth ? "long-width" : "short-width"}`} onClick={() => this.setState({increaseWidth: !this.state.increaseWidth})}></div>
</div>
);
}
In css file -:
.flexible-width{
background: red;
height: 50px;
}
.long-width{
width: 150px;
}
.short-width{
width: 50px;
}

For a simple width style do it like this. If you want to do extensive styling, shift to using CSS classes and changing classes at runtime.
import React from 'react';
class Component extends React.Component {
state = {
clicked: false
}
render() {
return ( <div>
<div style = {{ width: this.state.clicked ? "100px" : "103px" }>
//div content here
</div>
<button onClick = {() => this.setState({clicked: !this.state.clcked})}>
{ clicked ? "Reset" : "Increase Width"}
</button>
</div>)
}
}
export default Component;

Related

Why is my SVG not appearing when passed as prop?

I've looked through some of the other, similar questions on here, but none of the solutions seem to work for me.
I have a class component that represents a cell on a grid, and for each cell, I want there to be a specific background image that is revealed with the cell is hovered. The default component looks like this:
const Home = () => {
return (
<div id="main">
<Box title='About' svg={ BG1 } />
<Box title='Work' svg={ BG2 } />
<Box title='Portfolio' svg={ BG3 } />
<Box title='Resume' svg={ BG4 } />
</div>
);
};
I want to pass SVG components as background images, but can't seem to figure it out. I've imported each background image like this:
import { ReactComponent as BG1 } from './bg1.svg';
In the Box component, I've added state to handle some of the child elements, and the title prop seems to work out well, but the svg prop doesn't seem to work for me:
class Box extends React.Component {
constructor(props) {
super(props);
this.state = {
hovered: false,
};
}
hoverOn = e => {
this.setState({ hovered: true });
};
hoverOff = e => {
this.setState({ hovered: false });
};
render() {
const { hovered } = this.state;
const style = hovered ? { transform: 'translateY(-20%)', opacity: '.05' } : {};
return(
<section onMouseEnter={this.hoverOn} onMouseLeave={this.hoverOff}>
<a>
<div className="overlay" style={ style }></div>
<div className="bg" style={{ backgroundImage: `url(${this.props.svg})` }} ></div>
<div className="innerContent">
<h2>
{this.props.title}
</h2>
</div>
</a>
</section>
);
};
};
Whenever I inspect my developmental site, I just see <div class='bg'></div>. Any suggestions?
It's not working because BG1 is being set to the SVG file contents; not a link to the SVG file. I think you can just reference the filename in url("./bg1.svg") and it will expand it at build time. However, that probably won't work when you just pass in the filename as a prop. So - you could put the SVGs into the public folder and do something like this: {process.env.PUBLIC_URL + '/img/logo.png'}. Escape hatch doc.
With Vite you can do this: import modelGLB from './static/model.glb?url' to get the URL, but I forget the create-react-app equivalent.

Use event handler to change button appearance

I would like to build out my functional component BoldButton in which when the user clicks either the lowercase a or uppercase A, the text of the button is changed to bold. Below is my work in progress.
import React, { useEffect, useState } from 'react';
const BoldButon = () => {
const [color, setColor] = useState("black")
useEffect(() => {
})
const changeColor = () => {
setColor("black")
}
return (
<div>
<button text={{style:color}} onClick={changeColor}>
A
</button>
<button text={{style:color}} onClick={changeColor}>
a
</button>
</div>
)
}
export default BoldButon;
This was one problem I had when doing a mock technical interview so I would like to know how to solve it when using a functional component. **Not sure if I need to utilize useEffect in order to solve this.
Currently, there are a few problems with syntax to address:
text={{style:color}} not sure what this is supposed to do: text is not a standard or custom prop that you have set anywhere on the button component. Also, say you were to change the color of the button's text (which you could do with style={{color}} then you would be able to change it from let's say black to gray or black to yellow for different emphasis, but you seemed to say that you wanted different font-weight (bold vs not bold) is controlled by the CSS property font-weight (and that can be set in HTML or JSX through the style property that all elements can have.
if you have nothing in useEffect probably you can/should get rid of it. useEffect is useful for if the state of something changes and you need a side effect you can put it there (or it can be used as a strange componentDidMount which will only run after all parents have had their useEffect run.
For something like this it might be better to use a class and then you can set a number of style properties with a single string: like is-emphasized is a commonly used class that can change the font-weight to 600 and the color to black from a dark gray (if necessary).
You probably want individual states for each individual button (as you want each button to be bold if it was clicked: it seems based on your question).
.is-emphasized {
font-weight: 600;
color: #000;
}
But if not the simplest fastest way to approach this is:
<button
style={{fontWeight: bold ? 'bold' : 'normal'}}
onClick={changeWeight}
>
A
</button>
<button
style={{fontWeight: !bold ? 'bold' : 'normal'}}
onClick={changeWeight}
>
a
</button>
function BoldButon() {
const [capitalABold, setCapitalABold] = React.useState(false)
const [aBold, setaBold] = React.useState(false);
const changeColor = () => {
setColor("black")
}
return (
<div>
<button
className={capitalABold ? 'is-emphasized' : ''}
onClick={() => setCapitalABold(!capitalABold)}
>
A
</button>
<button
className={aBold ? 'is-emphasized' : ''}
onClick={() => setaBold(!aBold)}
>
a
</button>
</div>
)
}
ReactDOM.render(<BoldButon/>, document.getElementById("root"));
* {
color: #333;
font-size: 14px;
}
.is-emphasized {
color: #000;
font-weight: 600;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You don't need useEffect for this kind of functionality, you can utilize the useState hook to manage the variable that determines if the user has clicked on the button. Also, you can use the useMemo hook to keep the style object which returns either bold or normal. Please note that the toggleBold toggles the font weight so if a user is clicking multiple times it will switch between the bold and normal.
import React, { useState, useMemo } from 'react';
const BoldButon = () => {
const [boldA, setBoldA] = useState(false)
const [boldB, setBoldB] = useState(false)
const toggleBold = (buttonType) => () => {
if(buttonType === 'firstButton') {
setBoldA(prevState => !prevState)
} else {
setBoldB(prevState => !prevState)
}
}
const buttonStyleA = useMemo(() => {
return {fontWeight: boldA ? 'bold' : 'normal'}
}, [boldA])
const buttonStyleB = useMemo(() => {
return {fontWeight: boldB ? 'bold' : 'normal'}
}, [boldB])
return (
<div>
<button style={buttonStyleA} onClick={toggleBold('firstButton')}>
A
</button>
<button style={buttonStyleB} onClick={toggleBold('secondButton')}>
B
</button>
</div>
)
}
export default BoldButon;

CSS styling for a boolean React hook button

I have a simple message board with nested comments built in React. I'm trying to add a favorite button (just a button that toggles filled/unfilled contingent on its boolean value). Every tutorial I find involves making a new component, but I'd like to include this in my primary app code (using a hook/useState).
I've tried some various CSS stuff like active, etc. I'm rusty with CSS and a bit lost using it on JSX. Ideally, it'd be a star button that fills/unfills, but I want to figure out the simple basics here first.
function Toggle(props) {
const [toggleState, setToggleState] = useState(false);
function toggle() {
setToggleState(toggleState === false? true : false);
}
return (
<div {...props}>
<Button
className={`switch ${toggleState}`}
onClick={toggle}>
Favorite
</Button>
</div>
)
}
Toggle=styled(Toggle)`
//dunno what to put here
The button shows up just fine but nothing I try in the style will make it toggle colors (or filled/unfilled) on click. How can I do this?
First of all this syntax is more readable
function toggle() {
setToggleState(!toggleState);
}
then you can use this to change the class of your button
className={`switch ${toggleState ? "some_class" : "some_other_class"}`}
If you use styled-components, you can customize Button directly. In Tagged templates, switch styles according to prop.
Demo
const Button = styled(Icon)`
color: ${props => (props.filled ? 'pink' : 'transparent')};
-webkit-text-stroke: 2px pink;
overflow: hidden;
margin: 0 0.5em;
cursor: pointer;
`
function Toggle(props) {
const [toggleState, setToggleState] = useState(false)
function toggle() {
setToggleState(toggleState => !toggleState)
}
return (
<Button filled={toggleState} onClick={toggle}>
favorite
</Button>
)
}
Adapting based on props
Following your code:
const StyledToggle=styled(Toggle)`
&.true {
//true styles
}
&.false {
//false styles
}
`;
As you are using css in js you could also pass the state as a prop to the component and read it inside the tagged template:
return (
<div {...props}>
<Button
toggleState={toggleState}
onClick={toggle}>
Favorite
</Button>
</div>
)
}
const StyledToggle = styled(Toggle)`
font-weight: ${({toggleState}) => toggleState && "bold"};
`;

Call to setState in onDragStart causes DOM node to be deleted?

I use a list of styled components for displaying some info. I want this info to be sortable. The real problem I'm trying to solve is actually way more complex than what I'm demonstrating here. So any odd design choices are very specific to what I'm trying to do. I'm just mentioning it because the code I'm showing will be very simplified but it will also show some of these at first glance odd design choices.
I've read this article: https://medium.com/the-andela-way/react-drag-and-drop-7411d14894b9
Temitope Emmanuel (the author) did what I'm trying to achieve but with just a plain div. I don't know whether he tested all of what he proposes in his article.
Off to some code:
import React, { Component, Fragment } from 'react';
import styled from 'styled-components';
export default class SomeList extends Component {
constructor(props) {
super(props);
// in real problem all of these are props
// pulled off the state of a parent
this.state = {
dragging: false,
listOfChildrenInOrder: ['1', '2', '3'],
itemComponent: styled.div`
border: 1px solid black;
`,
};
}
render() {
const {
dragging,
listOfChildrenInOrder,
itemComponent: ItemComponent,
} = this.state;
const {
children,
} = this.props;
const Container = styled.div`
display: grid;
grid-template-rows: max-content;
grid-template-columns: repeat(${listOfChildrenInOrder.length}, max-content) 1fr;
`;
const Droppable = styled.div`
&:hover {
background-color: rgba(0,0,0,0.4);
}
`;
return (
<Container>
<Fragment>
{listOfChildrenInOrder.map(((cid, i) => (
<ItemComponent
draggable
key={`ic-${cid}`}
style={{
gridArea: `1 / ${i + 1} / span 1 / span 1`,
}}
onDragStart={(e) => {
this.setState({ dragging: true });
e.dataTransfer.setData('text/plain', `${cid}`);
}}
onDragEnd={() => {
this.setState({ dragging: false });
// doesn't even fire anymore
}}
>
{children.find(c => c.key === cid)}
</ItemComponent>
)))}
</Fragment>
<Fragment>
{dragging && listOfChildrenInOrder.map(((cid, i) => (
<Droppable
key={`d-${cid}`}
style={{
gridArea: `1 / ${i + 1} / span 1 / span 1`,
}}
onDragOver={(e) => {
e.stopPropagation();
e.preventDefault();
}}
onDrop={() => {
// do whatever (out of scope), doesn't get called anyway
}}
>
{children.find(c => c.key === cid)}
</Droppable>
)))}
</Fragment>
</Container>
);
}
}
I'm expecting the reconciler (Fiber) to update the DOM node without straight out replacing it in the middle of a drag operation. I'm using these things to act as highlighters. The real Problem I'm trying to solve actually makes a difference on where exactly stuff gets dropped, so the grid in the real problem is finer, with more droppables and one item component spaning multiple grid columns. Like I said: odd choices, but not without purpose.
Okay, I know now what was causing this whole operation to fail. The reason was dynamically creating new styled components in the render loop. Never do that. Just another rule of thumb learned.

React hide textarea but writable

I'm making text editor from bottom up.
I want to give the illusion that users are using textarea directly.
So I think that in one big <div/> put <textarea> and <p> and hide textarea and show value of <textarea> in <p>.
When user click word in <p>, Put cursor in appropriate textarea and write word
in textarea (<p> is used just to show result)
But when i give display: hidden for this, I cannot type anything in textarea.
How can i show illusion to user use textarea directly but not really.
class CodeEditor extends Component {
constructor(props) {
super(props);
this.state = {
textValue: ''
}
}
underline = () => {
let textVal = this.editor;
let Start = textVal.selectionStart;
let End = textVal.selectionEnd;
const oldState = this.state.textValue;
if (oldState.substring(Start,End) !== '') {
this.setState({textValue : oldState.substr(0,Start) + `<p style={{text-decoration-line:underline}}>` + oldState.substring(Start, End) + `</p>` + oldState.substr(End)})
}
};
render() {
const content = this.state.textValue;
return (
<div className={cx('text-editor')} onClick={() =>
this.editor.focus()}>
<textarea
className={cx('text-input')}
ref={ref => this.editor = ref}
value={this.state.textValue}
style={{display:'hidden'}}
onChange={(event) => {
this.setState({
textValue:event.target.value
});
}} />
<div className={cx('editor-buttons')}>
<button onClick={this.underline}> Underline </button>
</div>
<p dangerouslySetInnerHTML={{ __html: content }} />
</div>
)};
}
export default CodeEditor;
You could utilize contenteditable html5 feature:
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable
For you all looking for an answer for this, here's an approach you can take:
1) Textarea/input has a border, so hide it with border-style: none and hide when focus with textarea:focus, input:focus { outline: none; }.
2) Change the background color to be the same as parent component.
3) Done :)

Resources