I have a very simple application where I am trying to update the state of a parent component from a child component as follows:
import React from '../../../../../../../dependencies/node_modules/react';
import ReactDOM from '../../../../../../../dependencies/node_modules/react-dom';
class CalendarMain extends React.Component {
constructor() {
super();
}
handleClick() {
this.props.handleStateClick("State Changed");
}
render() {
return (
<div>
<div className="calendar">
{this.props.checkIn}
<button onClick={ this.handleClick.bind(this) }>Click Me</button>
</div>
</div>
)
}
}
class CalendarApp extends React.Component {
constructor() {
super();
this.state = {
checkIn: "Check-in",
checkOut: "Check-out",
dateSelected: false
};
}
handleStateClick( newState ) {
this.setState({
checkIn: newState
});
}
render() {
return (
<div>
<CalendarMain
checkIn = { this.state.checkIn }
handleStateClick = { this.handleStateClick.bind(this) }
/>
</div>
);
}
}
The error I am receiving is this.setState is not a function and I can't work out why. Any help would be much appreciated!
this is not auto-bound in ES6 style syntax.
Either:
Bind in constructor like so: this.func = this.func.bind(this)
Use arrow function syntax for the function in question like so: func = () => {};
More here: https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding
Use () => lambda to provide lexical scoping and bind correct value of this within the method handleStateClick():
handleStateClick = () => {
this.setState({
checkIn: newState
});
}
Related
I have some event listeners on my props
constructor(props) {
super(props);
Tts.addEventListener("tts-start", event =>
console.log("started"),
this.setState({ ttsStatus: "started" })
//how to call stopTTS
); ...}
so if I have a function outside the constructor
stopTTS() {
console.log("cali");
}
how to call a function when the eventListener gets triggered? cheers
First: If you can, use hooks instead.
A functional component that can do what you want could be:
import React, { useEffect, useState } from 'react'
const Component = () => {
const [ttsStatus, setTtsStatus] = useState('')
const stopTTS = () => {
console.log("cali");
}
// This useEffect will work as a componentDidMount
useEffect(() => {
Tts.addEventListener("tts-start", event => {
console.log("started"),
setTtsStatus("started")
stopTTS() // You can call stopTTS here
})
}, [])
return null
}
export default Component
Try to avoid creating classes, the React Hooks were a new addition in React 16.8. They let you use state and other React features without writing a class, so you can have the power of a class in a cleaner function. You can know more about it in https://reactjs.org/docs/hooks-overview.html
As i mentioned in the comment, you can call the class methods inside constructor like below snippet.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
window.addEventListener('click', (e) => {
if (this.state.count > 4) {
this.alertCount();
}
});
}
alertCount = () => {
alert('count has become 5')
this.setState({
count: 0
})
}
clickHandler = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
<div >
<div>
The count is now {this.state.count}
</div>
<button onClick = {
this.clickHandler
} id = "btn" > Click Me < /button>
</div >
);
}
}
ReactDOM.render( < App / > , document.getElementById('root'))
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I want to keep some functions outside of my component for easier testing. However, I cannot change state with these functions because they cannot reference the component's state directly.
So I currently have the hacky solution where I set the function to a variable then call this.setState. Is there a better convention/more efficient way to do this?
Example function code in Tester.js:
const tester = () => {
return 'new data';
}
export default tester;
Example component code in App.js (without imports):
class App extends Component {
constructor() {
super();
this.state = {
data: ''
}
}
componentDidMount(){
let newData = tester();
this.setState({ data: newData })
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
You could bind your tester function like this (this approach doesn't work with arrow functions):
function tester() {
this.setState({ data: 'new Data' });
}
class App extends Component {
constructor() {
super();
this.state = {
data: '',
};
this.tester = tester.bind(this);
}
componentDidMount() {
this.tester();
}
render() {
return (
<div>{this.state.data}</div>
);
}
}
But I would prefer a cleaner approach, where you don't need your function to access this (also works with arrow functions):
function tester(prevState, props) {
return {
...prevState,
data: 'new Data',
};
}
class App extends Component {
constructor() {
super();
this.state = {
data: '',
};
}
componentDidMount() {
this.setState(tester);
}
render() {
return (
<div>{this.state.data}</div>
);
}
}
You can pass a function to setState() that will return a new object representing the new state of your component. So you could do this:
const tester = (previousState, props) => {
return {
...previousState,
data: 'new data',
};
}
class App extends Component {
constructor() {
super();
this.state = {
data: ''
}
}
componentDidMount(){
this.setState(tester)
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
The reason being that you now have access to your component's previous state and props in your tester function.
If you just need access to unchanging static placeholder values inside of your app, for example Lorem Ipsum or something else, then just export your data as a JSON object and use it like that:
// testData.js
export const testData = {
foo: "bar",
baz: 7,
};
...
// In your app.jsx file
import testData from "./testData.js";
const qux = testData.foo; // "bar"
etc.
I have the following class
class MatchBox extends React.Component {
constructor(props) {
super(props);
this.countdownHandler = null;
this.showBlocker = true;
this.start = this.start.bind(this);
}
start() {
...
}
render() {
...
return (
<div style={ styles.mainContainer } className="fluid-container">
...
</div>
);
}
};
function mapStateToProps(state) {
...
}
function matchDispatchToProps(dispatch) {
...
}
export default withRouter(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(MatchBox));
which is used in this class
class GameBox extends React.Component {
constructor(props) {
super(props);
...
}
render() {
var mainElement = null;
switch(this.props.mainElement.element) {
case 'SEARCHING': mainElement = <SearchingBox gameType={ this.props.gameType }/>; break;
case 'MATCH': mainElement = <MatchBox ref='matchBox'/>; break;
default: mainElement = <SearchingBox/>;
}
return (
<div style={ styles.mainContainer } className="fluid-container">
{ mainElement }
</div>
);
}
};
function mapStateToProps(state) {
...
}
function matchDispatchToProps(dispatch) {
...
}
export default withRouter(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(GameBox));
And I can't get the ref of the object MatchBox. I tried with this.refs.matchBox and is null, also tried getting directly from ref(ref={(r) => { // r is null } }) and I don't know what to try anymore.
I'm using react-router-dom 4 and I don't know if function withRouter affect the outcome component.
It's not pretty, but I think this is the solution. withRouter exposes the child ref via a wrappedComponentRef callback, which gets us to the connect hoc. That exposes its child ref via getWrappedInstance if you pass the withRef attribute as you did. So you just have to combine both of those.
class GameBox extends React.Component {
matchboxRefCallback = (connectHOC) => {
this.matchboxRef = connectHOC ? connectHOC.getWrappedInstance() : null;
}
render() {
return <MatchBox wrappedComponentRef={this.matchboxRefCallback}/>;
}
}
Much more cleaner solution would be to create a HOC. which will forward the ref to actual component
const matchBoxHOC = (WrappedComponent) => {
class MatchBoxHOC extends React.Component {
render() {
const { forwardRef, ...rest } = this.props;
return <WrappedComponent {...rest} ref={forwardRef} />;
}
}
const WithRouterMatchBoxHOC = withRouter(MatchBoxHOC, { withRef: true });
return React.forwardRef((props, ref) => {
return <WithRouterMatchBoxHOC {...props} forwardRef={ref} />;
});
}
Call is like
export default matchBoxHOC(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(MatchBox));
I'm building a sidebar menu skeleton using ReactJs and need to understand the way to call a function inside ReactJs render() function.
The code is below:
import React from 'react';
var menuData = require("./data/admin.menu.json");
class SidebarMenu extends React.Component {
constructor(props) {
super(props);
this.state = { expanded: true };
this.buildItem = this.buildItem.bind(this);
};
buildItem(title, ref, icon) {
return (
<div className={"item" + this.props.key}>
<a href={ref}>{title}<i className={"fa " + icon} /></a>
</div>
);
};
render() {
return (
<div>
{
menuData.forEach(function (item) {
this.buildItem(item.title, item.ref, item.icon);
if (item.hasOwnProperty("submenu")) {
item.submenu.forEach(function (subitem) {
this.buildItem(subitem.title, subitem.ref, subitem.icon);
});
}
})
}
</div>
);
};
}
export default SidebarMenu;
The given code shows the following error:
Uncaught TypeError: Cannot read property 'buildItem' of undefined
How to properly call a function that will render data inside the ReactJs function ?
The this referenced when you try to call this.buildItem() refers to the anonymous function's context, not your React component.
By using Arrow Functions instead of functions defined using the function keyword inside the render() method, you can use this to reference the React component and its methods as desired.
Alternatively, you can use (function () { ... }).bind(this) to achieve the same result. But this is more tedious and the use of arrow functions is preferred.
Below is one solution, using fat arrow, AKA arrow functions:
import React from 'react';
var menuData = require("./data/admin.menu.json");
class SidebarMenu extends React.Component {
constructor(props)
{
super(props);
this.state = { expanded: true };
this.buildItem = this.buildItem.bind(this);
};
buildItem(title, ref, icon) {
return (
<div className={"item" + this.props.key}>
<a href={ref}>{title}<i className={"fa " + item.icon}/></a>
</div>
);
};
render() {
return (
<div>
{
menuData.forEach(item => {
this.buildItem(item.title, item.ref, item.icon);
if (item.hasOwnProperty("submenu"))
{
item.submenu.forEach(subitem => {
this.buildItem(subitem.title, subitem.ref, subitem.icon);
});
}
})
}
</div>
);
};
}
export default SidebarMenu;
Another solution would be:
render() {
return (
<div>
{
menuData.forEach(function (item) {
this.buildItem(item.title, item.ref, item.icon);
if (item.hasOwnProperty("submenu"))
{
item.submenu.forEach(function (subitem) {
this.buildItem(subitem.title, subitem.ref, subitem.icon);
}.bind(this));
}
}.bind(this))
}
</div>
);
};
}
But, IMO, the best solution would be to refactor the code using a component:
import React, {PropTypes, Component} from 'react';
const menuData = require('./data/admin.menu.json');
function MenuItem({key, ref, title, icon, submenu}) {
return (
<div className={`item${key}`}>
<a href={ref}>{title}<i className={`fa ${icon}`}/></a>
if (submenu) {
submenu.map((subitem) => <MenuItem {...subitem} />)
}
</div>
);
}
MenuItem.propTypes = {
key: PropTypes.string,
title: PropTypes.string,
ref: PropTypes.string,
icon: PropTypes.string,
submenu: PropTypes.array,
};
class SidebarMenu extends Component {
constructor(props) {
super(props);
this.state = {
expanded: true,
};
}
render() {
return (
<div>
{
menuData.map((subitem) => <MenuItem {...subitem} />)
}
</div>
);
}
}
export default SidebarMenu;
You can add this line:
render() {
let that = this
return (
and then instead of this.buildItem use that.buildItem or you may need that.buildItem.bind(that)
#File 1:
let ticketEnable = false;
export default class SupportTicketMain extends Component {
constructor () {
super();
}
render () {
let expandIcon = <DownIcon/>;
if (this.state.ticketDetailExpanded) {
expandIcon = <UpIcon/>;
}
return (
<Section className="ticketMain" primary={true}>
<TicketHeader expanded={ticketEnable}/>
</Section>
);
}
};
export function setTicketEnablement (value) {
ticketEnable = value;
}
#file 2:
import { setTicketEnablement } from file1;
export default class SupportTicketTabs extends Component {
constructor () {
super();
this.state = {
ticketDetailExpanded: false
};
this._expandClick = this._expandClick.bind(this);
}
_expandClick() {
this.setState({ticketDetailExpanded: !this.state.ticketDetailExpanded});
setTicketEnablement(this.state.ticketDetailExpanded);
}
render () {
let expandIcon = <DownIcon/>;
if (this.state.ticketDetailExpanded) {
expandIcon = <UpIcon/>;
}
return (
<Button className="expander" type="icon" onClick={this._expandClick}>
{expandIcon}
</Button>
);
}
};
Here a button click in supportTicketTabs class of #file2 will update global variable in #File1 , but SupportTicketMain render doesn't update if the global variable value changes! please guide me on this.
ticketEnable should be a prop passed into SupportTicketMain. The component that wraps both SupportTicketTabs and SupportTicketMain should be handing down a callback as a prop that modifies the value of ticketEnable (toggleTicketEnable) and the value of ticketEnable
class Main extends Component {
constructor (props) {
super(props);
this.onToggleTicketEnable = this.onToggleTicketEnable.bind(this);
this.state = {
ticketEnabled: false;
};
}
onToggleTicketEnable() {
this.setState({ ticketEnabled: !this.state.ticketEnabled });
}
render () {
return (
<App centered={false}>
<SupportTicketMain ticketEnable={this.ticketEnabled} />
<SupportTicketTabs onToggleTicketEnable={this.onToggleTicketEnable}/>
</App>
);
}
}