HolyMoly HolyMoly - 1 month ago 6
React JSX Question

How can I pass a locally created function and variable as props in a component?

I have a button component (a dumb component) and I would like to have an optional animation attached to it, and pass that animation down in props, to be triggered by a boolean value of another prop. I keep getting errors on trying to pass both.

Unknown prop 'ripple' on <button> tag. Remove this prop from the element.


const Button = (props) => {
{propOne, propTwo, ripple, ...remainingProps} = props

function coolAnimation () {
// cool ripple effect code
}

return (
<button
className={css( styles.button, secondary && styles.secondary)}
{...remainingProps}
ripple={false}
animation={coolAnimation}
>
{label}
{props.children}
</button>
)
}

export default Button


I have also tried creating the component as a class (in case it mattered):

class Button extends React.Component {
{propOne, propTwo, ripple, ...remainingProps} = props

_coolAnimation () {
// cool ripple effect code
}

render() {
return (
<button
className={css( styles.button, secondary && styles.secondary)}
{...remainingProps}
ripple={false}
animation={coolAnimation}
>
{label}
{props.children}
</button>
)
}
}

export default Button


I suspect I can't just add
ripple
into the const that is deconstructing the
props
object because it does not already exist on the object being deconstructed there. I have tried just extending the object using
this.props.ripple = false
and got an error saying the object is not extendable.

How do I properly add and pass these as props?

if it helps, ideally I would be able to do this:
<Button ripple={true} onClick={ripple && coolAnimation}></Button>
or something similar and have instances of the button that use the ripple animation and others that do not.

UPDATE:

So following the provided guide, I have a separate (parent) component that now takes in the Button component:

class RippleButton extends React.Component {

constructor(props){
super(props)
}

_animateRipple(e) {
// create animation
}

render() {
const {children} = this.props
return (
<Button
className={css(wrapperStyle.rippleWrapper)}
animation={this._animateRipple}
ripple={false}
>
{children}
</Button>
)
}
}

export default RippleButton


And I have the child Button component which is only rendering the html button:

const Button = props => {
const { label, secondary, ...remainingProps } = props

return (
<button
className={ css(
styles.button,
secondary && styles.secondary) }
{...remainingProps}
>
{label}
{props.children}
</button>
)
}

Button.PropTypes = {
label: React.PropTypes.string.isRequired,
secondary: React.PropTypes.bool,
}

export default Button


but I am still getting the same error
Unknown props
animation
,
ripple
on <button> tag
even though I am passing them from the parent Component, into the Button component (not the button tag directly as I was before)...thoughts?

Answer

It looks like you're getting a few patterns wrong here--you might want to consider hitting-up a good React tutorial. It'll take some time, but will save you tremendous amounts of frustration longer-term.

As to your question, a few points:

first, the error you're getting is because you're assigning properties on a regular html element, not a React component. The tag names are case sensitive, so <button> !== <Button>

second, you're skipping a few steps in achieving your goal. Unfortunately it's not as simple as just declaring an animation--the component has to be told when to animate, what to animate, etc. etc.

Here's a simple example of a button that grows when onMouseOver. I haven't tested this, so don't take it as gospel:

// parent component
class ButtonParent extends React.Component {
  constructor(props) {
  this.state = {
    isMouseOver: false
  }
  this.handleButtonHover = this.handleButtonHover.bind(this)
  }

  handleButtonHover() {
    this.setState({ isMouseOver: !this.state.isMouseOver })
  }

  render() {
    return (
      <div>
        <Button 
          handleButtonHover={this.handleButtonHover} 
          isButtonHovered={this.state.isButtonHovered}
        />
      <div>
    )
  }

// Button.jsx
const Button = (props) => {
  const {handleButtonHover, isButtonHovered, ...remainingProps} = props

  const style = {
    transform: props.ripple === true ? 'scale(1.5,1.5) : 'scale(1,1)',
    transition: 'all 0.5s ease-in',
  }

  return (
    <button
       onMouseEnter={props.handleButtonHover}
       onMouseLeave={props.handleButtonHover}
      {...remainingProps}
    >
      {label}
      {props.children}
    </button>
  )
}

export default Button

Do you see the pattern? the current state is held in the parent, which passes it, and a callback to the child. when the child sees the event, it uses the callback, which changes the state at the parent level, and affects (via the style object) button.

This type of handle event -> adjust state -> re-render pattern is critical to a React app, as is the use of ternary operators to handle events.

Comments