Abstracting an onClick function that also does state update - javascript

I'm trying to abstract away my onClick function(s) into its own file and then later use it in a component's button(s).
However the function needs to conditionally update the state of the component it is being used in.
export let ButtonClickHandlers = {
refreshDatabase: (url: string) => {
service.fetchJson<any>(url)
.then(res => {
if(res.isSuccessStatusCode){
//update state
}
else{
//update state
}
});
},
//more functions to be added later
}
In my component I want to do something like this:
import {ButtonClickHandlers} from '../ButtonClickHandlers';
<button onClick = {ButtonClickHandlers.refreshDatabase} />
How do I implement this functionality?

This is how i have done it, there might be better ways.I have imported the clickevent and passed the context as paremeter
clickevenhandler.js
const clickEvent = (context) => {
context.setState({ click: true });
}
export default clickEvent;
my test component
import clickEvent from './clickhandler';
class TestComp extends React.Component<any, any>{
constructor(props) {
super(props);
this.state = {
click: false
}
}
renderclicked() {
if (this.state.click) {
return (<div>clicked</div>);
}
else {
return (<div></div>);
}
}
render() {
return (<div>Test
<button onClick={() => clickEvent(this)}>click</button>
{this.renderclicked()}
</div>);
}
}

Related

API taking too long, map function firing before data loads

import React, { Component } from 'react';
import {withProvider} from './TProvider'
import ThreeCardMap from './ThreeCardMap';
class Threecard extends Component {
constructor() {
super();
this.state = {
newlist: []
}
}
componentDidMount(){
this.props.getList()
this.setState({newlist: [this.props.list]})
}
// componentDidUpdate() {
// console.log(this.state.newlist);
// }
render() {
const MappedTarot = (this.state.newlist.map((list, i) => <ThreeCardMap key={i} name={list.name} meaningup={list.meaning_up} meaningdown={list.meaning_rev}/>);
return (
<div>
<h1>Three Card Reading</h1>
<div>{ MappedTarot }</div>
</div>
)
}
}
export default withProvider(Threecard);
Hi, I'm trying to create a page that takes data from a tarot card API (https://rws-cards-api.herokuapp.com/api/v1/cards/search?type=major). Unfortunately by the time the data comes in, my map function has already fired. I'm asking to see if there is a way to have the map function wait until the data hits before it fires. Thanks!
Edit: getList function in the Context:
getList = () => {
console.log('fired')
axios.get('https://vschool-cors.herokuapp.com?url=https://rws-cards-api.herokuapp.com/api/v1/cards/search?type=major').then(response =>{
this.setState({
list: response.data
})
}).catch(error => {
console.log(error);
})
}
this.props.getList() is an async function. You are setting the list right after that call which is not correct.
You need to set it in the getList promise then() block.
getList() is an async function and update data for the parent component. So, my solution is just watching the list from the parent component if they updated or not, through getDerivedStateFromProps
class Threecard extends Component {
constructor() {
super();
this.state = {
newlist: []
}
}
// Set props.list to this.state.newList and watch the change to update
static getDerivedStateFromProps(nextProps, prevState) {
return {
newlist: nextProps.list
}
}
componentDidMount(){
this.props.getList()
// Removed this.setState() from here.
}
render() {
const MappedTarot = (this.state.newlist.map((list, i) => <ThreeCardMap key={i} name={list.name} meaningup={list.meaning_up} meaningdown={list.meaning_rev}/>);
return (
<div>
<h1>Three Card Reading</h1>
<div>{ MappedTarot }</div>
</div>
)
}
}
export default withProvider(Threecard);

React call parent component method from child component method [duplicate]

This question already has answers here:
How can I call parent method in a child React component?
(2 answers)
Closed 4 years ago.
I have a child component which is a Redux form and from it's handleSubmit method, I need to call a method on the Parent Component. I try to do this by passing a callback as props from the Parent and nothing happens.
I've seen that this method works only when a function is called directly with an event handler on the child component.
import Parent from './parent.js';
class Child extends React.Component {
constructor(props) {
super(props);
};
callCloseModal = () => {
this.props.closeModal();
}
handleFormSubmit (values) {
this.callCloseModal()
}
render() {
<form onSubmit=
{handleSubmit(this.handleFormSubmit.bind(this))}>
.....
</form>
}
}
class Parent extends React.Component {
constructor (props) {
super(props)
this.state = {
modalOpen: false,
}
}
.....
handleModalClose() {
this.setState({ modalOpen: false })
}
render() {
<Child closeModal={this.handleModalClose}> {this.props.children}</Child>
}
}
How can I call a method on the parent component from a method on a child component?
Edit: The method was correct but it was one level higher (Grandparent component)
In your onSubmit handler:
render() {
<form onSubmit=
{handleSubmit(this.handleFormSubmit.bind(this))}>
.....
</form>
}
You call handleFormSubmit, but in its definition:
handleFormSubmit (values) {
this.callCloseModal
}
You only reference the callCloseModal. Notice callCloseModal is defined as an arrow function:
callCloseModal = () => {
this.props.closeModal();
}
So you need to call it. Try:
handleFormSubmit (values) {
this.callCloseModal();
}
I guess this would work as expected. Just call callCloseModal as a function inside handleFormSubmit
class Child extends React.Component {
constructor(props) {
super(props);
};
callCloseModal = () => {
// ideally you want to check if props is a func
if (typeof this.props.closeModal === 'function') {
this.props.closeModal();
}
}
handleFormSubmit = (values) => { // no need to mix binding with arrow funcs
this.callCloseModal() // should be calling as a function
}
render() {
<form onSubmit=
{handleSubmit(this.handleFormSubmit)}>
.....
</form>
}
}
you just have to change the function from this
callCloseModal = () => {
this.props.closeModal();
}
to this
callCloseModal = () => {
this.props.handleModalClose();
}
Let me know if you face any other issue.

ReactJs - How to complete onClick before download - href

I have a simple React button component that when clicked should retrieve and download data on the client browser. The problem I am experiencing is that the download is triggered and the csv file downloaded before the data is passed into the href.
Here is my component:
import { Component } from 'react';
import { connect } from 'react-redux';
import { PropTypes } from 'prop-types';
import { ManageUsersSelectors } from 'selectors/Users';
import { BatchRoleActions } from 'actions/Users';
class UsersExportButton extends Component {
constructor() {
super();
this.state = {
users: ''
};
}
getUsers(){
const { userIds } = this.props;
BatchRoleActions.getAllRoleUsers(userIds)
.then((users) => {
this.setState({ users: users});
return this.state.users;
});
}
render() {
return (
<div className="roles-export-button">
<a className="button button-default" href={this.state.users} download={'roles.csv'} onClick={() => this.getUsers()} return true>Export Csv</a>
</div>
);
}
}
function mapStateToProps(state) {
const userIds = ManageUsersSelectors.batchUserIdsSelector(state);
return {
userIds: userIds
};
}
UsersExportButton.propTypes = {
text: PropTypes.string.isRequired,
data: PropTypes.array
};
export default connect(mapStateToProps)(UsersExportButton);
How can I get the getUsers()/onClick function to complete the data retrieval step before downloading?
When i debug my code I can see that the getUsers function returns data - however after the download is triggered
Make sure to bind this to your functions. In your constructor you can do:
constructor() {
super();
this.state = {
users: ''
};
this.getUsers = this.getUsers.bind(this);
}
or you can use the bind this function:
getUsers = () => {
const { userIds } = this.props;
BatchRoleActions.getAllRoleUsers(userIds)
.then((users) => {
this.setState({ users: users});
return this.state.users; // This should be removed, you can use this.state.users throughout this component.
});
}
Why not get the user data in the componentDidMount lifecycle method? It doesn't look like it needs to be called onClick.
{
// ...
componentDidMount() {
this.getUsers();
}
// ...
render() {
return (
<div className="roles-export-button">
<a className="button button-default" href={this.state.users} download={'roles.csv'}>Export Csv</a>
</div>
)
}
}
How about handling the default "link" behaviour manually to get more control? Also you should probably try to access state after setState has been executed via its callback.
e.g.
getUsers(cb){
const { userIds } = this.props;
BatchRoleActions.getAllRoleUsers(userIds)
.then((users) => {
// note the callback of setState which is invoked
// when this.state has been set
this.setState({ users: users }, cb);
});
}
const handleClick = () => {
this.getUsers(() => {
window.open(this.state.whatever)
})
}
<span onClick={handleClick}>Export Csv</span>

Component props.dispatch doesnt work. React redux

I need help. In my colorcontrol I am trying to do a this.props.dispatch(triggerFBEvent(fbID, method, params)) with no luck.
What works though is if I were to just do just triggerFBEvent(fbID, method, params). I am getting the error:
index.bundle.js:61968 Uncaught TypeError: this.props.dispatch is not a function
What I am trying to accomplish is to be able to send in new props with the line above, and then on
componentWillMount() {
this.props.dispatch(fetchFBEvent(this.props.fbID, "getColor"))
}
Call a custom service to update state with appropriate colors. But this.props.dispatch is not a function there either.
import React from 'react'
import { connect } from 'react-redux'
import {triggerFBEvent, triggerFBClearEvent, fetchFBEvent} from '../actions/functionblocksActions'
`
import { CustomPicker, HuePicker, SaturationPicker, SliderPicker, CustomPointer } from 'react-color';
#connect((store) => {
return {
fb: store.functionblocks.functionblock
}
})
export default class Functionblock extends React.Component {
constructor(props) {
super(props);
this.state = {
};
this._getType = this._getType.bind(this)
}
_getType (wanted) {
const { fbID, fbName, func } = this.props;
let type;
let types = {
'com.xxxx.xx.service.dal.functions.Alarm': function () {
type = <Alarm fbID={fbID} fbName={fbName}/>;
},
'com.xxxx.xxx.service.dal.functions.BooleanControl': function () {
type = <BooleanControl fbID={fbID} fbName={fbName}/>;
},
'com.xxx.xxxx.service.dal.functions.BooleanSensor': function () {
type = <BooleanSensor fbID={fbID} fbName={fbName} />;
},
'com.xxxx.xxx.service.dal.functions.ColorControl': function () {
type = <ColorControl func={func} fbID={fbID} fbName={fbName} />;
}
'default': function () {
type = <WakeUp fbID={fbID} fbName={fbName} />;
}
};
// invoke it
(types[wanted] || types['default'])();
// return a String with chosen type
return type;
}
render() {
const { fbID, fbName, func } = this.props;
const type = this._getType(func.serviceProperties["clazz"]);
return(
<div>
{type}
</div>
)
}
}
// Classes for the different functions.
class ColorControl extends React.Component {
componentWillMount() {
this.props.dispatch(fetchFBEvent(this.props.fbID, "getColor"))
}
constructor(props) {
super(props);
this.state = {
color: {
h: 150.3197479248047,
s: 0.5,
l: 0.5
}
}
this.onChangeComplete = this.onChangeComplete.bind(this);
}
componentWillReceiveProps(nextProps) {
alert("YEP")
// let home = this._getHome(nextProps.areas, nextProps.statics);
// if(home!=null){
// this.setState({
// inputHome: home.name,
// })
// }
}
onChangeComplete(color, event) {
let hsl = color.hsl;
let hue = color.hsl.h / 360;
let saturation = color.hsl.s;
let lightness = color.hsl.l;
this.setState({ color: hsl })
// Update props
let fbID = this.props.fbID;
let method = "setColor";
let params = {"hue": hue, "sat": saturation, "light": lightness};
this.props.dispatch(triggerFBEvent(fbID, method, params))
}
_defineFunction(){
}
render() {
return (<div>
<SliderPicker {...this.props}
pointer={ CustomPointer }
color={this.state.color}
onChangeComplete={ this.onChangeComplete }
direction={ 'horizontal' || 'vertical' }/>
</div>
)
}
}
Can anyone help me understand whats going wrong?
You need to connect ColorControl to Redux, otherwise it doesn't get a dispatch prop.
#connect()
class ColorControl extends React.Component {
Here is the codebase to use your actions without problems.
import * as actions from './YourActionsPath'
import { bindActionCreators } from 'redux'
#connect(
state => ({
yourDerivedState : state.somePath
}),
dispatch => ({
actions : bindActionCreators( actions, dispatch )
})
)
export default class YourClass extends Component {
someMethod(){
this.props.actions.yourAction() // call it with no problems
}
render(){
return (
// your html
)
}
}
I hope you get the idea. If you use this patterns, you won't have problems.
As you can use your derived state as this.props.derivedState, which you define in the connect, you can also use your actions you defined on the connect.
Besides you can use this.props.dispatch if you connected your component. In case you need it as in your case, but this makes the code less clear and leads to maintainance problems.
import { createStore } from 'redux'
let store = createStore(//define reducer,preload state and enhancer)
//call action
store.dispatch(triggerFBEvent(fbID, method, params))

Bind in constructor or fat arrow in class

So i'm wondering if there is a difference between this:
import React, { Component, PropTypes } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.state = {
page : 1
};
}
nextPage = () => {
this.setState({ page: this.state.page + 1 });
}
previousPage= () => {
this.setState({ page: this.state.page - 1 });
}
render() {
const { page } = this.state;
return (
<div>
<H1>{page}</H1>
<Button onClickPrevious={this.previousPage} onClickNext={this.nextPage} />}
</div>
);
}
}
Or
import React, { Component, PropTypes } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.nextPage = this.nextPage.bind(this);
this.previousPage = this.previousPage.bind(this);
this.state = {
page: 1
};
}
nextPage() {
this.setState({ page: this.state.page + 1 }); }
previousPage() {
this.setState({ page: this.state.page - 1 }); }
render() {
const { page } = this.state;
return (
<div>
<H1>{page}</H1>
<Button onClickPrevious={this.previousPage} onClickNext={this.nextPage} />}
</div>
);
}
}
I'm wondering if it's the same in performance this to every function or are there any other benefits?
a bit of further reading(https://medium.com/#esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f#.khf30fuaq)
The best place to bind your event handlers is your constructor. This way your event handler has its context bound to the component instance.You can access props and state and call setState or forceUpdate from such bound handler.
Binding with arrow functions have their own advantages as well.
Arrow functions always gets the context from where they have been defined. So in fact, this example is equivalent to:
The arrow function syntax is a way of defining function with a syntax like this:
change = (ev) => this.setState({ text: ev.target.value });
It is a more concise way than writing a function(ev) { .... } statement. If you don’t provide { and } brackets after the => arrow, such function is a single expression which is returned instantly. So this desugars to something like:
change = function(ev) { return this.setState({ text: ev.target.value }); }.bind(this);
And hence both of .bind() and arrow function cause a new function to be created
Concluding, the way you want to bind your function depends on your use case.
For more details you can read up this article:

Categories

Resources