Hi I have a query about JSX syntax support, will it be possible to give jsx expressions inside js object?
I have a Button component like this,
import React, {Component} from 'react';
export default function ButtonComponent(props){
return (
<button id={props.id} type="button" onClick={props.onClick} onMouseDown={props.onMouseDown} >
{props.value}
</button>
)
}
It will be used in some other component like bellow,
<ButtonComponent id="first" value="First" onClick={this.clickHandler} onMouseDown={this.mouseDownHandler}/>
As you can observe there is an array pattern getting created for events. It might grow with onMouseOver, onMouseUp etc.. events.
So I was just wondering if I could pass all the events as one object and attach them with spread operator?
Like I will modify the ButtonComponent as,
import React, {Component} from 'react';
export default function ButtonComponent(props){
return (
<button id={props.id} type="button" {...props.events} >
{props.value}
</button>
)
}
And use this component like below,
<ButtonClass id="first" value="First"
events={{onClick:{this.clickHandler}, onMouseDown:{this.mouseDownHandler}, onMouseOver:{this.mouseOverHandler}}}/>
But it throws error saying invalid syntax.
If I update the events object like below, it passes the syntax validation but events does not execute.
<ButtonClass id="first" value="First"
events={{onClick:(this.clickHandler), onMouseDown:(this.mouseDownHandler), onMouseOver:(this.mouseOverHandler)}}/>
Is there and way to achieve this pattern with out attaching all the events in the ButtonComponent?
Sorry for the long post..
You can define event listeners like an object and pass it to component with rest-spread operator. For example
const EVENTS = {
onMouseOver: () => console.log("mouseover"),
onMouseOut: () => console.log("mouseout"),
onClick: () => console.log("click"),
...
}
class MyCoolComponent extends React.Component {
render() {
return <button {...this.props.events}>{this.props.children}</button>
}
}
ReactDOM.render(
<MyCoolComponent events={EVENTS}>Some children</MyCoolComponent>,
document.body
)
However, this is not very convenient, because you always can mistype the event prop name (e.g. onCLick or something) and get the error in runtime. But it is possible :)
Related
I create a very generic Modal that can get different header, body and footer, but also different arguments for their Reactstrap components (I'm using Reactstrap to create the Modal but the question needn't be specific to solve a Reactstrap problem).
My GenericModal.js code looks like:
class GenericModal extends React.Component{
render(){
return(
<Reactstrap.Modal {...this.props.other} />
<Reactstrap.ModalHeader {...this.props.headerArgs}>{this.props.header}</Reactstrap.ModalHeader>
<Reactstrap.ModalBody {...this.props.bodyArgs}>{this.props.body}</Reactstrap.ModalBody>
<Reactstrap.ModalFooter {...this.props.footerArgs}>{this.props.footer}</Reactstrap.ModalFooter>
</Reactstrap.Modal>);
}
}
And so I call this class like this:
<GenericCard {...{other: other, headerArgs: headerArgs, bodyArgs: bodyArgs, footerArgs: footerArgs,
cardheader:header, cardbody:body, cardfooter:footer}} />
Now I know that this method works because I've tried it with className, for example:
const bodyArgs = {className: 'my-5'};
I want to also be able to pass an onClick function - but not merely the function (as we can see in this question), but the whole thing: onClick=foo().
I'm having a bit of a problem understanding how I can put the onClick method inside a json-style format like I did with className.
I can't write an anonymous function for the onClick inside const bodyArgs = {...}, and writing it as
const bodyArgs = {onClick: {foo}};
Provides an undefined foo. I also can't put this.foo because it's an unexpected syntax as well.
Any thoughts?
Welp, found the solution moments after I posted this.
Just didn't need the {} curly brackets.
const bodyArgs = {onClick: this.foo};
Does the job.
Thought I'd keep it here in case anyone stumbles into this issue.
This should work as you have explained but I cannot fully know without the whole example. Here is a working bit of code and a codesandbox link of what you are tying to do.
import React from "react";
import "./styles.css";
class ClickExecutor extends React.Component {
render() {
return (
<div>
<h4>Click Executor</h4>
<div>
<button onClick={() => this.props.bodyArgs.alert1()}>One</button>
<button onClick={() => this.props.bodyArgs.alert2()}>Two</button>
</div>
</div>
);
}
}
class GenericModal extends React.Component {
alert1 = () => {
alert("Alert 1");
};
alert2 = () => {
alert("Alert 2");
};
render() {
const bodyArgs = {
alert1: this.alert1,
alert2: this.alert2
};
return (
<div>
<h1>Generic Modal</h1>
<ClickExecutor
{...{
bodyArgs: bodyArgs,
otherProps: "other various properties ..."
}}
/>
</div>
);
}
}
export default function App() {
return (
<div className="App">
<GenericModal />
</div>
);
}
Working Demo LINK : https://codesandbox.io/s/smoosh-frost-rj1vb
I'm trying to focus on the second 'CurrentTextField' after the first 'CurrentTextField' change your value,
but return the error 'inputRef.current is null'
import React, {useRef } from 'react';
import CurrencyTextField from "#unicef/material-ui-currency-textfield";
export default function Clientes() {
let refInput = useRef(null)
return(
<div>
<CurrencyTextField
onChange={(e) => refInput.current.focus()}
/>
<CurrencyTextField
inputRef={refInput}
/>
</div>
)
}
UPDATE 2
I checked again and saw that uses the TextField component from material-ui/core which has a inputProps prop, so this will work
<CurrencyTextField inputProps={{ref:refInput}} />
UPDATE
I just checked the CurrencyTextField source code and it doesn't handle refs
Since you are using the custom component CurrencyTextField you should check if it handles references, when using normal HTML tags you would use the prop ref
export default function Clientes() {
let refInput = useRef(null)
return(
<div>
<CurrencyTextField
onChange={(e) => refInput.current.focus()}
/>
<-- normal input will work -->
<input
ref={refInput}
/>
</div>
)
}
Try to do it like if it were a regular HTML tag to see if it works.
That looks kind of weird. I checked the React documentation and looks good useRef React. Maybe can bring us more details about the library because maybe could be related to your problem
I would like to pass a custom props to the onClick handler.
I cannot return an inline function as below, because I will have to later fire redux action creator as part of the handler (async action is not allowed)
onClick={()=>this.handleClick("v")}
Using middleware seems an overkill to me..
For simplicity purpose, please just ignore the redux part. Just say we can't use inline arrow function like this.
The example code below is just a POC approach that I borrow from input component, where value is an inherited props.
I am OK with any props("custom" as I said)
class Test extends React.Component {
handleClick = (event) => {
console.log(event.target.value);
}
render() {
return (
<div>
<label
value="v"
onClick={this.handleClick}
>
TEST Label
</label>
</div>
)
}
I expect console log to output a custom value -- "v"
your options are really limited. You should either use the inline arrow function and handle you async whatever problem in some other way or you should find a way to keep your state updated with current value of your label value. If it was an input onChange = {this.handleChange} would do it. this way your code will look like this:
handleClick(){
const {value} = this.state;
doSomething(value)
}
updateValue(input){
/* this.setState({
value : input
})*/
//in your case :
this.setState({
value : 'v'
})
}
render(){
return(
<label
value= {this.state.value}
onClick={this.handleClick}
>Text</label>
)
}
hope this helps
use mapDispatchToProps to pass action to the components and call it similar to above.
import React from "react";
import { action1 } from "./actions";
const App = ({action1})=> (
<button onClick={()=>action1("one")}>Click Me</button>
);
export default connect(null, {action1})(App);
I have to use a react component that I cannot modify. It's from an external source, due to changes. This could also be a component from a npm package that I import. This is what it looks like, a simple button:
class Button extends React.Component {
// ... more code above
render() {
const { onClick, disabled, children} = this.props;
return (
<button className={this.getClasses()} onClick={onClick} disabled={disabled}>
{this.props.symbol && <Icon symbol={this.props.symbol} />}
{children}
</button>
);
}
}
How can I add some functionality with no access to the file (I can create my own component that extends the button)? For example, I want a type prop in there. I thought I can just create a <ButtonExtend onClick={resetState} type="button />.
How can I do this? Ideally I would like to make this even more flexible, so I can also do: <ButtonExtend onClick={resetState} type="submit" name="extended button" />.
I would expect the html to render all the properties from <Button> with my additional html attributes. So I want to use the functionality of the original and my additional props. Or it this not even possible, to change the render method of another component, if the component doesn't make it possible?
Although public methods and properties of a component are accessible by refs (https://reactjs.org/docs/refs-and-the-dom.html) the pattern are you looking for is High Order Components (HOC, https://reactjs.org/docs/higher-order-components.html)
Unless a component was designed for customization, there is no straightforward way to do this.
Button is an example of badly designed component because it doesn't accept additional props. An issue and PR could be submitted to the repository in order to address original problem.
In extended component, this can be fixed by passing props from extended component.
Parent render result could be modified:
class ButtonExtend extends Button {
// ... more code above
render() {
const button = super.render();
const { symbol, children, ...props } = this.props;
return React.cloneElement(button, {
children: [
symbol && <Icon symbol={symbol} />,
...children
],
...props
});
}
If an element that needs to be modified is nested, this may become messy and result in unnecessarily created elements.
A cleaner way is to paste render in extended component and modify it:
class ButtonExtend extends Button {
// ... more code above
render() {
const { symbol, children, ...props } = this.props;
return (
<button className={this.getClasses()} {...props}/>
{symbol && <Icon symbol={symbol} />}
{children}
</button>
)
}
}
This way it can be used as
<ButtonExtend onClick={resetState} type="submit" name="extended button" />
in my react's App.js's return i am currently calling this.searchVenues() in a way that works but is messy and i know there is a better way. The searchVenues() function lives in App.js and I have buttons that need to be in their own component, then just <ButtonComponent/> instead of:
render() {
return (
<div className="App container-fluid">
<Navbar/>
<div className="row">
<div className="col-xs-3">
<button onClick ={() => this.searchVenues("yoga+coffee", "5")}>5</button>
<button onClick ={() => this.searchVenues("yoga+coffee", "10")}>10</button>
<button onClick ={() => this.searchVenues("yoga+coffee", "15")}>15</button>
<SideBar {...this.state} handleListItemClick={this.handleListItemClick}/>
</div>
<div className="col-md-9 full-height">
<Map {...this.state}
handleMarkerClick={this.handleMarkerClick}/>
</div>
</div>
<Footer/>
</div>
);
}
but when i do this.searchVenues("yoga+coffee", "5") does not work, understandably so. What's the best or a better way to make it work? How do i access the function from another file ( component )?
I believe you want to declare your searchVenues func in App.js component like this:
searchVenues = (arg1, arg2) => {here is the body of your function}
... and pass it down to the ButtonComponent using props:
<ButtonComponent searchVenues={this.searchVenues}
Once you are in your stateless ButtonComponent, you can create a new function inside it if you want to avoid anonymous functions in your render (you can read on it here)
const searchVenues = (arg1, arg2) => {
return event => {props.searchVenues(arg1, arg2);}
}
... and add it to the onClick event:
<button onClick ={searchVenues('coffee+yoga', props.value)}>{props.value}</button>
If you want your buttons to live in another component, but receive handlers from another component, you can pass them down through props.
import React, { Component } from 'react'
import MyButton from './MyButton'
export default class App extends Component {
buttonClickHandler= () => {
alert('I have been clicked!')
}
render = () => (
<MyButton onClickHandler={this.buttonClickHandler} />
)
}
And here is the MyButton component file:
import React from 'react'
const MyButton = (props) => (
<button onClick={props.onClickHandler}>Click Me for an Alert!</button>
)
export default MyButton
You could either have searchVenues defined in your ButtonComponent or pass it to ButtonComponent as a prop. Then in your ButtonComponent render function you would do the same thing:
<button onClick ={() => this.searchVenues("yoga+coffee", "15")}>15</button>
or if it is passed as a prop
<button onClick ={() => this.props.searchVenues("yoga+coffee", "15")}>15</button>
I will also add that #Bryan is absolutely right about services. Lets say for instance you have a table in a database called Product. Well, you don't want to implement getAllProducts() in every component that needs a list of products. Instead, you would create a class called ProductService where getAllProducts() would be defined. Then, for any component that needs a list of products, you would import the ProductService class and call ProductService.getAllProducts().
Hope that helps.
If you want to execute methods from any component, and the result of those methods will change the global state of the app, you might benefit from using actions.
Whether you're using flux or redux architecture, actions can be triggered from any part of the app, and perform changes in the global state, this changes will be reflected on any component that is listening to this state.
https://reactjs.org/blog/2014/07/30/flux-actions-and-the-dispatcher.html
https://redux.js.org/basics/actions