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"};
`;
Related
I have input form and input tag as a button.
Before I was able to make a button which changed styling according to clicking on it but now i try to make this input gray until user check the checkbox. I tried to use <Show> and when property but i cant use when on <Show> tag. Then I tried to use onChange property and it seem to not give me any errors. I just don't understand how can I change styling inside class=" and then connect it to checkbox function? My checkbox is made by using createSignal
Any ideas?
<input
onChange={functionForStyling}
name="submit"
type={"submit"}
value={"Sign up"}
class=""
/>
Assigning a class to a checkbox element is not different than assigning it to any html element.
Here is how you can assign a class to an html element conditionally.
import { createSignal, JSX } from 'solid-js';
import { render } from 'solid-js/web';
const App = () => {
const [isChecked, setIsChecked] = createSignal(true);
const toggle = () => setIsChecked(c => !c);
const handleChange: JSX.EventHandler<HTMLInputElement, Event> = (event) => {
setIsChecked(event.currentTarget.checked);
};
return (
<div>
<style>{`
input.checked {
transform: rotate(45deg);
}
input.unchecked {
transform: rotate(45deg);
}
`}</style>
<input
type='checkbox'
class={isChecked() ? 'checked' : 'unchecked'}
checked={isChecked()}
onChange={handleChange}
/>
{` `}
<button onclick={toggle}>Toggle</button>
</div>
);
};
render(() => <App />, document.body);
https://playground.solidjs.com/anonymous/163ffec6-1293-4702-9ef6-0425461328c3
Please keep in mind that styling a checkbox is not that straightforward. You need to use pseudo selectors etc.
I have a React component called beercard which populates an item for me and adds a like button, here's the Beercard file:
function Beercard(props) {
const [active, setActive] = useState(false);
const changeLike = () => {
setActive(!active)
}
return (
<BeerCard>
<Content>
<Lockup text={props.description} tag="h3" title={props.title}/>
</Content>
<ImagContainer>
<Like liked={active} />
<Image url={props.image}/>
</ImagContainer>
</BeerCard>
)
}
Then I have the Like component, which I will use to handle the onClick:
const Icon = styled.svg`
width: 32px;
height: 32px;
stroke: white;
stroke-width: 2px;
fill: ${props => props.liked ? 'white' : 'transparent'};
`
const like = (props) => {
return (
<LikeButton onClick={props.action}>
<Icon liked={props.liked}>
<path
id="heart-icon"
d="M16,28.261c0,0-14-7.926-14-17.046c0-9.356,13.159-10.399,14-0.454c1.011-9.938,14-8.903,14,0.454
C30,20.335,16,28.261,16,28.261z"
/>
</Icon>
</LikeButton>
)
}
It all works fine ie with the css for filled and unfilled, then if I set the state to true in the beercard file it sets the heart to active/fills it. However the onClick function is not doing anything and I'm not sure how to communicate between the two files to add this event. It's a simple on/off toggle with true and false but I don't know how to handle the event, can somebody see what I need to do?
If I understand correctly, you just need to pass along the desired function as a property to the Like component. You're actually 90% there, but you're calling props.action in onClick, but passing nothing to the action property.
Since you already have everything, you can change the line where you use your Like component to:
<Like liked={active} action={changeLike} />
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;
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;
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.