How to Call multiple Functions inside a Render in React/Jsx - reactjs

I am trying to return multiple functions inside render, but its not getting call from another component:---
class OptionPanel extends React.Component {
constructor(props){
super(props);
this.onRemove = this.onRemove.bind(this);
this.onArrowUp = this.onArrowUp.bind(this);
this.onArrowDown = this.onArrowDown.bind(this);
}
onRemove(event){
event.preventDefault();
this.props.dispatch.deleteElement(this.props.DesignPanel.selectedElemId);
{/*event.currentTarget.parentElement.parentElement.parentElement.remove();*/}
}
onArrowUp(event){
event.preventDefault();
this.props.dispatch.moveElementUp(this.props.DesignPanel.selectedElemId);
}
render(){
return(
<div>
{this.onRemove()}
{this.onArrowUp()}
</div>
)
} }
Would this be the correct way of calling a function inside a render method?

What you're doing in your code-snipped is, that you're directly calling the methods (onRemove() and onArrowUp(). They will be called and whatever they return will be rendered as well.
So:
Would this be the correct way of calling a function inside a render method?
Yes, this is the right way, however it only makes sense, if these functions will return any content, that should be rendered (like additional components).
If you want to bind these functions to certain events, you have to put the functions as attributes to components.
For a button it would be:
<button type="button" onClick={this.onClickHandle()}>Click me</button>
So whenever the button will be clicked, the method "onClickHandle" will be called.

You don't need to extend React.Component if you only want to export functions from it
You can create a separate file action.js
and have
export function onRemove(event, dispatch, DesignPanel){
event.preventDefault();
dispatch.deleteElement(DesignPanel.selectedElemId);
}
export function onArrowUp(event, dispatch, DesignPanel){
event.preventDefault();
dispatch.moveElementUp(DesignPanel.selectedElemId);
}
and then import it in the component from where you need to call them like
import {onArrowUp, onRemove} from './path/to/action.js'

You miss binding the event handlers to html tags events
f.e
If you want to bind it to onClick and keyUp events
render(){
return(
<div>
<button onClick="{this.onRemove()}">
<input keyUp="{this.onArrowUp()}">
</div>
)
}
}

Related

this undefined in arrow function

I am creating a component that contains of a Form with a Submit button. In the OnSubmit of the Form I call an arrow function. Inside this function I call the object "this" but I get the error message that it is undefined. However, if I just do a console.log(this) in the OnSubmit of the form instead of calling the arrow function, this is defined. Anyone knows how to solve this issue ? I am actually following a react/ethereum course and even though the code of the instructor works in his video, his code doesn't work when I use it.
import React, {Component} from 'react';
import { Button, Form, Input, Message } from 'semantic-ui-react'
class CampaignNew extends Component{
state = {
minimumContribution: "",
errorMessage: ""
}
onSubmit = async (event) => {
event.preventDefault();
this.setState({errorMessage: "test error"});
}
render(){
return (
<div>
<Form onSubmit={this.onSubmit} error={this.state.errorMessage}>
<Form.Field>
<label>Minimum contribution</label>
<Input
labelPosition="right"
value={this.state.minimumContribution}
onChange={event => this.setState({minimumContribution: event.target.value})}
style={{width: "250px"}} placeholder='0' />
</Form.Field>
<Button type='submit' primary>Create</Button>
</Form>
</div>
)
}
}
export default CampaignNew;
Your code runs fine for me, but there is some missing information on how you are creating your component. It seems you may not be using Create-React-App. Your issue is with method binding in a class component.
The React documentation (https://reactjs.org/docs/handling-events.html) states:
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
This is not React-specific behavior; it is a part of how functions work in JavaScript. Generally, if you refer to a method without () after it, such as onClick={this.handleClick}, you should bind that method.
If calling bind annoys you, there are two ways you can get around this. If you are using the experimental public class fields syntax, you can use class fields to correctly bind callbacks:
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => { console.log('this is:', this); }
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
This syntax is enabled by default in Create React App.
Also the error prop of the Form component should be a boolean and the async word is not required in the code you provided.
That's related to how this value is defined in javaScript. You should know that "this" value is defined by who calls it in runtime.
In Class, "this" value is the same this value of its parent. In this code, "this" value is CampaignNew class object.
class CampaignNew extends Component{
onSubmit = async (event) => {
event.preventDefault();
this.setState({errorMessage: "test error"});
}
But "this" value is different in code below.
<Form onSubmit={this.onSubmit}
This is because Form element calls onSubmit which means this value in onSubmit is the same value of Form element's "this" and Form element doesn't have this value so this is why the value's undefined.
I found the problem. It was my version of NextJS. If I downgrade it to an earlier version, everything works fine. No idea why it breaks in the new version.
Working version: "next": "^11.1.3"

How to maintain state

I have a text box with some text which I get after doing ajax call.
When ever a user keys some text I dispatch the value to state.
This renders the current text area and cursor goes to front of the line.
How can I avoid re-rendering. How should I maintain state.
class Design extends React.Component{
constructor(){
super();
}
handleMessageChange(e){
let msg = e.target.innerText;
this.props.dispatch({type:'design_changed',designObj:{property:'message',value:msg}})
}
render(){
return (
<React.Fragment>
<Panel header="Message" key="2">
<div>
<div className="userscoop-content-editable-cls" contentEditable="true" onKeyUp={this.handleMessageChange.bind(this)}>{this.props.message}</div>
</div>
</Panel>
</React.Fragment>
}
function mapStateToProps(state){
let settings = state.HelloBar.template.settings;
return {
background_color: settings.background_color,
action_color:settings.action_color,
message:settings.message,
button_color:settings.button_color,
input_placeholder:settings.input_placeholder,
goal:state.HelloBar.goal
}
}
If you use shouldComponentUpdate lifecycle method and compare the new and old state you can cancel the render.
Alternatively you could first try PureComponent and see if that works.
If you still have problems might consider looking into saving the cursor state also. [from here]
You can use components's shouldComponentUpdate() method to define whether or not it should rerender when some prop changes. You can write to to ignore this change.
Try Redux-Form,if you are dealing with components which requires continuous action on fields.
Or use document.addEventListener in every actions which you fire by passing keyUp or keyDown

Access to internal function from externally loaded HTML in React component

I have the following use case.
Some HTML from a third party source is loaded inside my React component:
class MyComponent extends Component {
render() {
return (
<div
dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
/>
);
}
}
Inside the externally loaded HTML a click event exists for a specific span, which is supposed to call a callback function that exists in my application.
<span onclick="myCallback(param1='asd', param2=123, param3='asdas')">
Click me!
</span>
Where should I put this myCallback function?
If I place it inside the component class I get the following error when clicking the span, because as I understand the function is not visible to the externally loaded HTML: Uncaught ReferenceError: myCallback is not defined at HTMLSpanElement.onclick
My other idea was to add the function to the window object window.myCallback = ... inside my main index.js file to be loaded every time the app loads. This way it works but I have two issues.
My understanding is that this is not the correct React way to do it.
Whenever I click the span element the callback function is triggered twice and I cannot understand why.
Any suggestions?
Using "dangerouslySetInnerHTML" is ..."dangerous" as its name ^^, which is actually not pure React way, either.
However, If you have to do it, you can do something like this (take advantage of built-in jQuery inside React be default)
=====
EDITED VERSION FROM HERE: (use only 1 component)
export default class MyComponent extends Component {
componentDidMount() {
// using jQuery to manipulate DOM element form third-party source
// NB. you may think of using setTimeout here, to wait for the external source to be fully loaded here, of course it's not so safe
// but anyway, we are using "dangerouslySetInnerHTML" anyway => quite dangerous, though ^^
// setTimeout(function(){
$(document.findElementsByTagName("span")[0]).click(function(e){
// or perhaps $("#spanID").click if you can, just be careful between DOM element and react component
e.preventDefault();
// DO SOMETHING HERE, just like you do in the window.onload function
// or maybe you also need to get param values by getting $(this).data("...") or $(this).attr("ATTRIBUTE-NAME")
return false;
});
// });
}
render() {
return (
<div
dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
/>
);
}
}
=====
OLD ANSWER FROM HERE: (use 2 components)
ParentComponent:
export default class MyComponent extends Component {
constructor(props) {
super(props);
this.callbackOnThisComponent = this.callbackOnThisComponent.bind(this);
}
callbackOnThisComponent(param1, param2, param3) {
// do whatever you like with the above params
}
render() {
return (
<ChildComponent triggerCallbackOnParent={this.callbackOnThisComponent} />
);
}
}
ChildComponent:
export default class ChildComponent extends Component {
componentDidMount() {
// using jQuery to manipulate DOM element form third-party source
let that = this;
// NB. you may think of using setTimeout here, to wait for the external source to be fully loaded here, of course it's not so safe
// but anyway, we are using "dangerouslySetInnerHTML" anyway => quite dangerous, though ^^
$(document.findElementsByTagName("span")[0]).click(function(e){
// or perhaps $("#spanID").click if you can, just be careful between DOM element and react component
e.preventDefault();
that.props.triggerCallbackOnParent(param1, param2, param3);
// or maybe you need to get param values by getting $(this).data("...") or $(this).attr("ATTRIBUTE-NAME")
return false;
}, that);
}
render() {
return (
<div
dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
/>
);
}
}
I just use the main React's idea, which is passing props downward to children components, and when you want to trigger a function upward from child component, create a callback function on parent. For your or anyone else's reference, this is my demonstration on how to pass callback function from parent to multi-level-children components:
Force React container to refresh data
Re-initializing class on redirect
if this doesn't work yet, feel free to show me some error logs, thanks

how to Trigger componentWillReceiveProps on React-storybook

I am trying to test my UI component on React Storybook (link). However, I will like to add a button so when I pressed it, it will pass a new props to the component that is already rendered, (hence trigger the lifecycle method componentWillReceiveProps). However, I don't know how to do that. I will really appreciate any helps on this, thank you.
You need to find the first shared parent of the button and the component that you're trying to trigger componentWillReceiveProps for.
For instance, suppose your structure looks like this:
<SomeComponent>
<AnotherComponent someProp={someValue} />
<AThirdComponent>
<button />
</AThirdComponent>
</SomeComponent>
SomeComponent is the first shared parent of AnotherComponent and the button.
Once you've found your shared parent, add some state and a method to it:
class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.updateButton = this.updateButton.bind(this);
this.state = { buttonWasClicked: false };
}
updateButton() {
this.setState({ buttonWasClicked: true});
}
}
Now pass the updateButton method down to your button in AThirdComponent and attach the state to AnotherComponent:
render() { // SomeComponent's render method
return (
<div>
<AnotherComponent buttonWasClicked={this.state.buttonWasClicked} />
<AThirdComponent updateButton={this.updateButton}`/>
</div>
);
}
And in AThirdComponent, attach the updateButton handler to the button:
<button onClick={this.props.updateButton} />
Now when you click the button, SomeComponent's state will update, causing it to pass new props to AnotherComponent.

react redux pass this.props to actions from component

In my component class I have this.props which contains history object which contains transitionTo function.
render() {
console.log(this.props)
return (
<div>
<button onClick={actions.myAction}>My button</button>
</div>
Here my button is calling myAction which when completes wants to route to some other location. I want to access this transitionTo function in my action and performs something like router.transitionTo("/page1")
For this I should get history object which contains transitionTo function. So how can I pass this.props.history object to actions. When I do onClick={actions.myAction(this.props.history)} my button is not being rendered..
Any help
The answers above are correct, but fall appart when migrating to "Stateless Components" this quickly falls apart. My suggestion is using a higher-order function to take care of both the handling of preventing default click action and supplying the correct arguments. If you're already using ES2015 (through Babel for example), this could be this simple:
const preventDefault = (fn, ...args) => (e) => {
e.preventDefault();
fn(...args);
};
Then in your components call like so:
const Item = ({
item,
openItem
}) => (
<div>
<h1>{item.title}</h1>
<a href="#" onClick={preventDefault(openItem, item)}>Open Item</a> // on click will call `openItem(item)`.
</div>
)
I do not agree with your approach, but find bellow the solution to your problem:
handleBtnClick() {
actions.MyAction(this.props.history);
}
render() {
return (
<div>
<button onClick={ this.handleBtnClick.bind(this) }>My button</button>
</div>
);
}
When your button is clicked the function handleBtnClick() is invoked and there you can call your action and pass any props you want.
onClick expects a function expression (something that evaluates to a function expression). You are not supposed to make complex calls in there such as actions.myAction(this.props.history).
I would suggest you to consider using react-router-redux and handle url transitions using routeActions and custom redux middleware. I can expand this approach if you are interested.
EDIT:
If you use react-router-redux then you should take advantage of methods in routeActions. The method you need in this case is push(). So, if you want to transition to the /user/account url you just call dispatch(routeActions.push('/user/account')) and react-router-redux will handle the transition.
Eg:
import { routeActions } from 'react-router-redux';
// Action creator
function mySuperAction(flag) {
return (dispatch, getState) => {
dispatch(routeActions->push('/user/account'));
}
});
You need to have your store properly setup with react-router-redux in order for this to work.
Or from a component:
import { routeActions } from 'react-router-redux';
import { connect } from 'react-redux';
import React from 'react';
class MyComponent extends React.Component {
handleClick() {
this.props.dispatch(routeActions.push('/user/account'));
}
render() {
return (
<div>
<button onClick={ this.handleClick.bind(this) }>My button</button>
</div>
);
}
}
export default connect()(MyComponent);

Resources