I have this component:
<Button type="submit" { invalid ? 'primary': null }>
this component is styled component:
import styled from 'styled-components';
export const Button = styled.button`
font-size: 15px;
padding: 0.25em 1em;
border: solid 1px ${(props) => {
let color;
if (props.primary) {
color = 'red';
} else {
color = '#ffffff';
}
return color;
}};
`;
I get this error:
Syntax error: Unexpected token ^invalid, expected ... (64:54)
I need just to send a property 'primary' if invalid is true, to get this:
<Button type="submit" primary/>
I don't want write:
primary = { invalid }
the component calls this button is:
import React from 'react';
import { Button } from './layouts/cssstyled';
const getConditionalProps = ( props) => {
// fill your `invalid` variable in this function or pass it to it
const myprops = {};
myprops.primary = true ;
myprops.secondary = false;
return myprops;
}
const Form = (props) => {
console.log('form props');
console.log(props);
const { handleSubmit, invalid, pristine, reset, submitting, t } = props;
return (
<form onSubmit={handleSubmit}>
<p>Invalid? {JSON.stringify(invalid)}</p>
<Button type="submit" disabled={submitting} primary={invalid ? "" : null} >
Buton styled component does not work
</Button>
<button primary={invalid ? "" : null}> button native works</button>
<div className="formError">
{pristine}
{invalid && t('form.haserrors')}
</div>
</div>
</form>
);
};
export default reduxForm({
form: 'CustomerForm', // a unique identifier for this form
})(Form);
You can use boolean values, so your conditional will become like this:
<Button type="submit" primary={ !!invalid } >
Or if you don't want your button to have a primary value when false, you can do this:
const button = invalid ? <Button type="submit" primary > : <Button type="submit" >
I suggest using the React-JS Spread Attributes. It is great tool to spread props on a component with conditional behavior:
class MyComponent extends React.Component {
// ...
getConditionalProps() {
const props = {};
// fill your `invalid` variable in this function or pass it to it
if (invalid) {
props.primary = true;
}
return props
}
render() {
return (
<Button type="submit"
{ ...this.getConditionalProps() } />
);
}
}
Another solution is conditional rendering:
class MyComponent extends React.Component {
// ...
render() {
return (invalid ?
<Button type="submit" primary />
:
<Button type="submit" />
);
}
}
Please note that there may be a better solution to your problem: you need to handle the behavior of the Button component from inside the component itself instead of trying to decide from the parent component whether to send a specific prop or not. But the solution above does exactly what you are requiring.
Related
import React, { useState } from "react";
function App() {
const [headingText, setHeadingText] = useState("Hello");
const changeColor = { backgroundColor: "" };
function handleClick() {
setHeadingText("Submitted");
}
function handleMouseOver() {
changeColor.backgroundColor = "black";
}
function handleMouseOut() {
changeColor.backgroundColor = "white";
}
return (
<div className="container">
<h1>{headingText}</h1>
<input type="text" placeholder="What's your name?" />
<button
style={changeColor}
onClick={handleClick}
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
>
Submit
</button>
</div>
);
}
export default App;
ERROR:Cannot assign to read only property 'backgroundColor' of object '#'
I was trying to create a kind of form, in which the submit button should change to black on hover and to white on getting out.Can anyone help it out.
I tried your code but I am not getting the error you mentioned.
ERROR: Cannot assign to read-only property 'backgroundColor' of object '#'`
Also changing the constant changeColor will have no impact on the button as it will not cause the re-render. You need to use it as state variables.
const [changeColor, setChangeColor] = useState({ backgroundColor: "" });
function handleMouseOver() {
setChangeColor({
backgroundColor: "black"
});
}
function handleMouseOut() {
setChangeColor({
backgroundColor: "white"
});
}
But this can also be achieved using :hover selector as follows
.button_submit:hover {
background-color: black;
}
for the button
<button
className="button_submit"
onClick={handleClick}
>
Submit
</button>
I am using draft.js to develop a Rich text editor. I want the user to be able to keep typing once the Italic button is clicked. And inline styling should be applied until the user disable the italic button. Clicking on the button make the cursor to focus out of the editor. I created a ref and called the focus() function on the current ref and then called moveFocusToEnd on on edotorState. This does not work as expected. How do I achieve this behavior?
ReactJS
import React from 'react';
import { Editor, EditorState, RichUtils } from 'draft-js';
import { Button, Icon } from 'antd';
function MyEditor() {
const ref = React.useRef(null);
const [editorState, setEditorState] = React.useState(
EditorState.createEmpty()
);
const handleKeyCommand = command => {
const newState = RichUtils.handleKeyCommand(editorState, command);
if (newState) {
setEditorState(newState)
return "handled"
}
return "not-handled";
}
const onItalicClick = event => {
ref.current.focus()
EditorState.moveFocusToEnd(editorState)
setEditorState(RichUtils.toggleInlineStyle(editorState, 'ITALIC'))
}
const onUnderLinkClick = event => {
event.preventDefault()
setEditorState(RichUtils.toggleInlineStyle(editorState, "UNDERLINE"))
}
const onBoldClick = event => {
event.preventDefault()
console.log(event)
setEditorState(RichUtils.toggleInlineStyle(editorState, "BOLD"))
}
return <div>
<div>
<Button
onClick={onItalicClick}
>
<Icon type="italic" />
</Button>
<Button
onClick={onUnderLinkClick}
>
<Icon type="underline" />
</Button>
<Button
onClick={onBoldClick}
>
<Icon type="bold" />
</Button>
</div>
<Editor
editorState={editorState}
onChange={editorState => setEditorState(editorState)}
handleKeyCommand={handleKeyCommand}
ref={ref}
/>
</div>;
}
export default MyEditor;
SCSS
.wrapper {
border: 1px solid #e2e2e2;
padding: 10px;
margin-bottom: 20px;
}
selectionState = this.state.editorState.getSelection()
selectionState=selectionState.merge({'forceKey':xxxx, focusOffset:5})
here you can set focusOffset to be the text length of that block.
I'm attempting to have an icon's state change depending on whether or not there's data present. For example, if the 'Likes' counter is greater than 0 than the circle will be filled in, otherwise, it will be an empty circle.
I have not been able to figure out how to render this via useState
import React, { useState } from "react";
import ActivityIconEngaged from "./ActivityIconEngaged";
import { Modal, Button } from "react-bootstrap";
import PropTypes from "prop-types";
import data from "../Assets/ActivityData";
import { faCircle } from "#fortawesome/pro-regular-svg-icons";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
class ActivityIcon extends React.Component {
constructor(props) {
super(props);
this.state = {
activities: data.activities,
activity: data.activities[0]
};
}
render() {
const { activities, activity } = this.state;
return (
<div>
{activities.map(activity => (
<ActivityIconData key={activity._id} activity={activity} />
))}
</div>
);
}
}
function ActivityIconData({ activity }) {
const { index, likeCount } = activity;
const [show, setShow] = useState(false);
const handleClose = () => {
setShow(false);
};
const [trigger, setTrigger] = useState({ showContent: true });
const showContent = trigger;
const openModal = e => {
e.preventDefault();
setShow(true);
};
if ({ likeCount } > 0) {
setTrigger(!trigger);
}
return (
<>
<div id={`activity-${index}`}>
<span onClick={openModal} className="mr-1">
{showContent ? (
<FontAwesomeIcon
icon={faCircle}
size="2x"
className="ActivityIconDefault"
/>
) : (
<ActivityIconEngaged />
)}
</span>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Activity</Modal.Title>
</Modal.Header>
<Modal.Body>likes: {likeCount}</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>
</>
);
}
ActivityIconData.propTypes = {
activity: PropTypes.object.isRequired
};
export default ActivityIcon;
The result that I'm currently getting is that the modal successfully opens when the circle is clicked, HOWEVER, the circle is not filled when the number of likes exceeds 0.
I think you're declaring too many pieces of state to handle this. You don't need a likeCount as well as a trigger variable with a showContent property.
If you want to use a ternary operator to show the icon, you can just replace showContent with an expression for likeCount:
{likeCount > 0 ? <FontAwesomeIcon icon={faCircle} size="2x" className="ActivityIconDefault" /> : <ActivityIconEngaged />}
In first look, your condition is always false because you making a comparison between object and a number.
Two distinct objects are never equal for either strict or abstract comparisons.
// instead of { likeCount } > 0, which always results false
if (likeCount > 0) {
setTrigger(!trigger);
}
I have an object with icons available to some button.
const icons = {
check: 'icon-CheckSmall',
chat: 'icon-ChatMedium',
investors: 'icon-InvestorsMedium',
download: 'icon-DownloadMedium',
};
const Button = (props) => {
const {
buttonType,
buttonText,
onClick,
disabled,
} = props;
return (
<button
style={ icons }
type={ buttonType }
onClick={ onClick }
disabled={ disabled && 'disabled' }
>
{ <FormattedMessage id={ buttonText } /> }
</button>
);
};
I want to create prop types for component with keys array
Button.propTypes = {
icon: PropTypes.oneOf(Object.keys(icons)),
};
In this case, Object.keys does not work.
Is there anyone who manage to implement dynamic prop types in the component?
I am building an app in React, that is connected to an API I have written before. Buttons are renderizing but all of them change at the same time. I need advice about how can I write my code in order to separate the functionality.
My app renderize with a .map the same number of Buttons as appointments which is an array. All of them change when this.state.shown change but I need to separate all the buttons in order to only show the one that I clicked. Right now, when I clicked in one of them, this.state.shown change its value so all the buttons change because all depends of the same variable. I am looking for advices about how I can separate this.
class AppointmentsList extends Component {
constructor(props) {
super(props);
this.state = {
appointments: [],
isLoading: false,
shown: false, //Variable to know if a button need to change and render the component
customerUp: false
}
this.toggleCustomer = this.toggleCustomer.bind(this);
//this.showCustomer = this.showCustomer.bind(this);
}
toggleCustomer() {
this.setState({
shown: !this.state.shown
})
} //This function change the value of shown when a Button is clicked.
render() {
const {appointments, isLoading} = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown ? <CustomerView id={app.customer.id} /> : null }
</div>
)}
</div>
)
}
How can I reorganize my code in order to render the Buttons separately?
Thanks in advance.
Method 1: You can make shown state a object like:
state = {
shown:{}
}
toggleCustomer(id) {
const updatedShownState = {...this.state.shown};
updatedShownState[id] = updatedShownState[id] ? false : true;
this.setState({
shown: updatedShownState,
})
} //This function change the value of shown when a Button is clicked.
render() {
const {appointments, isLoading} = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown[app.customer.id] ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer(app.customer.id) }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown[app.customer.id] ? <CustomerView id={app.customer.id} /> : null }
</div>
)}
</div>
)
}
Method 2: Make a separate component for Button and Customer
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<Appointment key = {app.id} app = {app} />
)}
</div>
)
}
class Appointment extends Component {
state = {
shown: false,
}
toggleCustomer() {
this.setState({
shown: !this.state.shown
})
}
render() {
const { app } = this.props;
return (
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown ? <CustomerView id={app.customer.id} /> : null }
</div>
)
}
}
Let me know if it works and the method you prefer.
You can create a separate component for your button (buttonComponent) inside your AppointmentsList component and pass the shown has props and the in componentDidMount of buttonComponent copy the props to the state of buttonComponent.
This way each button will have its own state, which manages shown.
Button component:
import react from 'react';
interface buttonComponentProps{
shown: boolean;
}
interface buttonComponentState{
shown: boolean;
}
class buttonComponent extends react.Component<buttonComponentProps,{}>{
constructor(props:buttonComponentProps){
super();
this.state{
shown:props.shown
}
}
....
}
export default buttonComponent;