Every post or tutorial on how to toggle css in React assumes that I'm using a class component. However, I'm using a function component and don't have access to this and state. Can somebody tell me how to toggle a CSS style for a JSX element in a React function component?
import React from 'react';
import { Row, Container, Col } from 'react-bootstrap';
const FilterBar = ( {eventFilters, setEventFilters} ) => {
const someFunction = () => {
// code in here
}
return(
<div className="row-fluid" >
<Container fluid >
<Row className="justify-content-md-center mb-2 mt-2">
<Col onClick={someFunction}> <button value="ALL"> All </button> </Col>
<Col onClick={someFunction}> <button value="WORKSHOP"> Workshop </button> </Col>
<Col onClick={someFunction}> <button value="MINIEVENT"> Minievent </button> </Col>
</Row>
<Row className="justify-content-md-center mb-2">
<Col onClick={someFunction}> <button value="SPEAKER"> Speaker </button></Col>
<Col onClick={someFunction}> <button value="MEAL"> Meal </button> </Col>
<Col onClick={someFunction}> <button value="OTHER"> Other </button> </Col>
</Row>
</Container>
</div>
);
}
export default FilterBar;
Toggling some style via click event object. You can use the event target style property to change the current textDecoration property to "line-through" or nothing ("").
const toggleStrikethrough = (e) => {
e.target.style.textDecoration =
e.target.style.textDecoration === "line-through" ? "" : "line-through";
};
function App() {
const toggleStrikethrough = (e) => {
e.target.style.textDecoration =
e.target.style.textDecoration === "line-through" ? "" : "line-through";
};
return (
<div className="App">
<div onClick={toggleStrikethrough}>ALL</div>
<div onClick={toggleStrikethrough}>WORKSHOP</div>
<div onClick={toggleStrikethrough}>MINIEVENT</div>
<div onClick={toggleStrikethrough}>SPEAKER</div>
<div onClick={toggleStrikethrough}>MEAL</div>
<div onClick={toggleStrikethrough}>OTHER</div>
</div>
);
}
Note: This is generally frowned upon though and considered an anti-pattern as you are directly manipulating the DOM. The better and more react way is to maintain a toggled "state" in local component state for each element you want to toggle style (or anything really) of.
Related
I am changing the state with onClick and adding a border to the data of that state name. But when I click the other boxes, the previously added border is not removing. Here is my code;
const [toggleState, setToggleState] = useState(null);
return (
<React.Fragment>
<Col sm="6">
<Card className="text-center">
<CardBody onClick={() => setToggleState(user.name)} className={toggleState === user.name ? 'selectedItem' : '' + ' cursorPointer'} >
<Row onClick={event => props.onChange('true')}>
<Col sm="4">
</Col>
</Row>
</CardBody>
</Card
</Col>
</React.Fragment>
Hard to say what the className you're looking to set is, but Unmitigated seems to have a good idea about that issue. I also noticed that you don't close your
</Card
correctly on the third row from the bottom, this might affect how the page behaves as well.
I created one class base component and one functional component. There are three buttons inside the functional component and I called that component to my class based component.
The functional component:
function PanelButton(props){
return (
<div>
<Button.Ripple
color="success"
type="submit"
style={{margin:"5px", width:"110px"}}
>
Submit
</Button.Ripple>
<Button.Ripple
color="primary"
id="clearButton"
type="button"
style={{margin:"5px", width:"110px"}}
>
Clear
</Button.Ripple>
<Button.Ripple color="danger" type="button" style={{margin:"5px", width:"110px"}}>
Close
</Button.Ripple>
</div>
)
}
export default PanelButton;
The class base component, in which I imported the functional component into:
import PanelButton from '../../components/customzied/PanelButton';
class TicketNew extends React.Component{
state = {
alertOption:[],
}
clickClear = () => {
console.log("ok");
}
render() {
const rqst = this.state.rquirdSate;
return (
<Card>
<Formik>
{ ({ errors, touched}) => (
<div>
<Form onSubmit={handleSubmit}>
<CardHeader>
<PanelButton />
</CardHeader>
<CardBody>
<Row />
</CardBody>
</Form>
</div>
)}
</Formik>
</Card>
)
}
}
export default TicketNew;
When I click the button(id = "clearButton") from functional component, I need to run the Click clear function in Class component.
You can pass onClick callback handlers as props to PanelButton to attach to each button's onClick prop. Pass clickClear as callback for clear button.
PanelButton
function PanelButton(props) {
return (
<div>
...
<Button.Ripple
color="primary"
id="clearButton"
type="button"
style={{ margin: "5px", width: "110px" }}
onClick={props.onClear} // <-- attach callback to button's onClick handler
>
Clear
</Button.Ripple>
...
</div>
);
}
TicketNew
class TicketNew extends React.Component {
state = {
alertOption: []
};
clickClear = () => {
console.log("ok");
};
render() {
const rqst = this.state.rquirdSate;
return (
<Card>
<Formik>
{({ errors, touched }) => (
<div>
<Form onSubmit={handleSubmit}>
<CardHeader>
<PanelButton onClear={this.clickClear}/> // <-- pass this.clickClear to onClear prop
</CardHeader>
<CardBody>
<Row></Row>
</CardBody>
</Form>
</div>
)}
</Formik>
</Card>
);
}
}
You can pass the clickClear function as props to the PanelButton component. The PanelButton code will look like the following,
function PanelButton(props){
return(
<div>
<Button.Ripple color="success" type="submit" style={{margin:"5px", width:"110px"}}>
Submit
</Button.Ripple>
<Button.Ripple color="primary" id="clearButton" onClick={props.onClickCallback} type="button" style={{margin:"5px", width:"110px"}}>
Clear
</Button.Ripple>
<Button.Ripple color="danger" type="button" style={{margin:"5px", width:"110px"}}>
Close
</Button.Ripple>
</div>
)
}
And the TicketNew code will look like this,
...
<CardHeader>
<PanelButton onClickCallback={this.clickClear.bind(this)} />
</CardHeader>
...
I'm incredibly new to react and web development in general and I wanted to start by making a very simple shopping cart app. I found a nice tutorial on youtube for it, and am now trying to expand on it a bit. I wanted to add a search bar that filters out elements on the home-page as you type in it.
const Home = (props) => {
function handleChange(e){
console.log(e.target.value);
}
return (
<div>
<Row align="middle" className='title'> **search bar**
<Input placeholder="Search for a school" onChange={handleChange} />
</Row>
<Row align="middle" className='title'>
<Divider orientation="left"><b>Category E Schools</b></Divider>
</Row>
<Row align="middle" className='container'>
**Every item for sale is in a Col element**
<Col className = "Princeton University" xs={0} md={11} align="left">
<div className='image'>
<img src={Princeton} alt="Princeton University" />
<h3>Princeton</h3>
<h3>$1100.00</h3>
<button onClick={() => props.addBasket('princeton')} className='addToCart cart1' href='#'>Add to Cart</button>
</div>
</Col>
I'm using AntDesign Row-Col components, and my thought was to define a className for each Col. I was hoping that with the className I could implement the handleChange function to directly remove elements whose classNames don't contain the letters typed into the input bar. Sorry for the beginner-level work going on here.
Pat of the genius of react is that you can abstract your components and render them dynamically. to do this I would create a reusable component for all items and then render them dynamically.
Example:
first create an item component
const Item = (props) => (
<Col className = "Princeton University" xs={0} md={11} align="left">
<div className='image'>
<img src={props.image} alt={props.title} />
<h3>{props.title}</h3>
<h3>{props.cost}</h3>
<button onClick={() => props.addBasket('princeton')} className='addToCart cart1' href='#'>Add to Cart</button>
</div>
</Col>
)
}
add your items to state and render your state dynamically
also here I added state to search and set up two way binding between my input and my state value
i also added the filter method to my dynamic rendering of the items
i would recommend adding a library like loadash and using the debounce function so that the results re-render once 350ms after the user finishes typing instead of re-rendering on every single keystroke from the user
const Home = (props) => {
const [items, setItems] = useState([
{name: Princeton, price: 140000.00, img: http:princetonimages.com/f313b},
])
const [search, setSearch] = useState('')
const renderItems = (items) => (items.map(item => <Item {...item} />))
return (
<div>
<Row align="middle" className='title'> **search bar**
<Input
placeholder="Search for a school"
onChange={(e) => setSearch(e.target.value)}
value={search}
/>
</Row>
<Row align="middle" className='title'>
<Divider orientation="left"><b>Category E Schools</b></Divider>
</Row>
<Row align="middle" className='container'>
{renderItems(state.items.filter(search))}
</Row>
)
}
It is a bit hard to explain what it is exactly I am trying to do, but it should be pretty obvious by looking at the code.
Here is the full code for the react component, take note that the endpoint that populates the prop is missing as it hasnt been developed yet.
import React, { useState } from 'react';
import { Modal, ModalHeader, ModalBody, Button, Row, Col, Container } from 'reactstrap'
import '../../scss/hitchpin/star-rating.scss'
const ReviewModal = (props: Props) => {
const {
offering,
ratings,
comments,
response,
} = props
const [modal, setModal] = useState(false);
const [sellerReviews, setSellerReviews] = useState(false);
const toggle = () => {
setModal(!modal);
if (document.getElementById('seller').onclick) {
setSellerReviews(!sellerReviews);
}
}
return (
<div>
<Button id="seller" className="btn-sm ml-2 btn btn-secondary" onClick={toggle}> Seller Review Details </Button>
<Button className="btn-sm ml-2 btn btn-secondary" onClick={toggle}> Buyer Review Details </Button>
<Modal isOpen={modal} toggle={toggle}>
<ModalHeader toggle={toggle}>Review Details</ModalHeader>
<ModalBody>
<div>
<Container>
<Row>
<Col>As a Seller</Col>
</Row>
</Container>
</div>
<div className="p-3">
<h3>{offering.title}</h3>
<p>Category: {offering.category}</p>
<Container>
<Row>
<Col xs="6">Overall</Col>
<Col xs="6" class="Stars" style={'--ratings:' ratings.overall}></Col>
</Row>
<Row>
<Col xs="6">Response Time</Col>
<Col xs="6" class="Stars" style={--ratings: ratings.response}></Col>
</Row>
<Row>
<Col xs="6">Friendliness</Col>
<Col xs="6" class="Stars" style={--ratings: ratings.friendliness}></Col>
</Row>
<Row>
<Col xs="6">Punctuality</Col>
<Col xs="6" class="Stars" style={--ratings: ratings.punctuality}></Col>
</Row>
{sellerReviews && (
<Row>
<Col xs="6">Quality of Product/Service</Col>
<Col xs="6" class="Stars" style={--ratings: ratings.quality}></Col>
</Row>
)}
</Container>
<div>
<p>{!sellerReviews ? "Buyer's Comments:" : "Seller's Comments"}</p>
<textarea readOnly>
{comments.message && (
{comments.message}
)}
</textarea>
</div>
<div>
<p>{sellerReviews ? "Buyer's Response:" : "Seller's Response"}</p>
<textarea readOnly>
response.message && (
{response.message}
)}
</textarea>
</div>
</div>
</ModalBody>
</Modal>
</div>
);
}
export default ReviewModal
The issue I am having is, in the <Col> tags with the style attribute, I need to pass the prop ratings.category to the --ratings css variable. I know that if the number was static I could easily pass --ratings: 2.5, which is how i tested the code. However, in implementation, the floating point number will be variable and changing. Hence the issue.
First of all, I believe that
<Col xs="6" class="Stars" style={'--ratings:' ratings.overall}></Col>
should be
<Col xs="6" class="Stars" style={{'--ratings': ratings.overall}}></Col> (notice extra braces)
Also, if values are numeric, React might add 'px'. You might want to convert to string:
<Col xs="6" class="Stars" style={{'--ratings': String(ratings.overall)}}></Col>
UPD
Here is an example with latest React version (old versions do not support css variables via style prop): https://codesandbox.io/s/shy-star-mxu74
I have a "sign up" button. When it is clicked, I would like for it to render a new component, called "SignUp". I would like for the new component to replace the "sign up" button.
Currently, I am using setState so that when the button is clicked, a new component is rendered. However, the button is not being replaced, the "SignUp" component is just rendered beside the button. What may be a good approach for me to replace the button with the new component being rendered?
I have provided my code below:
export default class SignUpSignIn extends Component {
constructor() {
super();
this.state = {
clicked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
clicked: true
});
}
render () {
return (
<div id="SignUpSignInDiv">
<Col md="12" sm="12" xs="12" className="text-center">
<div onClick={this.handleClick}>
{this.state.clicked ? <SignUp /> : null}
<Button id="SignUpButton" color="primary"> Sign Up </Button>
</div>
</Col>
</div>
)
}
}
Well, you're not rendering both components conditionally, only the SignUp. Instead of having null in your ternary, render the sign in button when state.clicked === false:
render () {
return (
<div id="SignUpSignInDiv">
<Col md="12" sm="12" xs="12" className="text-center">
{this.state.clicked ? (
<SignUp />
) : (
<Button id="SignUpButton" color="primary" onClick={this.handleClick}> Sign Up </Button>
)}
</Col>
</div>
)
}
one way to do it is like this , i didnt test it yet but it should work
render () {
let show = <div></div>
if(this.state.clicked){
show = <SignUp />
}
return (
<div id="SignUpSignInDiv">
<Col md="12" sm="12" xs="12" className="text-center">
{show}
<Button id="SignUpButton" color="primary" onClick={this.handleClick}> Sign Up </Button>
</Col>
</div>
)
}