React states remain undefined - reactjs

I'm trying to set the in property of a bootstrap <Collapse> tag to true on a button click. But when I try to reference my is_open state its undefined.
class Graph extends Component {
constructor(props) {
super (props);
this.state = ({
is_open: false,
});
}
click_open = () => {
console.log(this.is_open); // logs undefined
this.setState({ is_open: !this.is_open });
}
render() {
return (
<div className='container>
<button onClick={this.click_open}>TAB</Button>
<Collapse in={this.is_open}></Collapse>
</div>
)
})
}
No matter what I do my state stays undefined. What am I missing here?

You're missing state
Change your code to be:
click_open = () => {
console.log(this.state.is_open);
this.setState({ is_open: !this.is_open });
}

Related

Loading indicator when button is clicked (reactjs and ant design)

I made a button that changes its label from 'extract' to 'extracted' when clicked then it becomes disabled. What I want to do now is for it to have a loading indicator when clicked and stop when the button is disabled.
I tried
document.getElementById(inputID).setAttribute("loading", "true");
when button is clicked but found out that it doesn't work on buttons. I am also trying right now to use setState with my
document.getElementById("btnTesting").innerHTML = "EXTRACTED";
class DashboardPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
}
onClickBtn = () => {
this.setState({ loading: true});
document.getElementById("btnTesting").innerHTML = "EXTRACTED";
document.getElementById("btnTesting").setAttribute("disabled","true");
}
render() {
return (
<Button id="btnTesting" onClick={this.onClickBtn} loading={this.state.loading}>EXTRACT</Button>
)
}
}
I expect to have a loading indicator when clicked then stop when button is disabled. But the screen turns blank. I look in the console and saw this error
Uncaught DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
The above error occurred in the component:
blah .
blah .
Uncaught DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
Because you're using React, you don't need to use the innerHTML or setAttribute to change button text or disabled state.
You can use state varibles buttonText, initialised to "Extract" and isDisabled, initialised to false, and you can change the state after the button is done executing.
class DashboardPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
buttonText: "EXTRACT",
isDisabled: false
};
}
onClickBtn = () => {
this.setState({ loading: true });
setTimeout(() => {
this.setState({
buttonText: "EXTRACTED",
isDisabled: true,
loading: false
});
}, 2000);
};
render() {
return (
<Button
id="btnTesting"
onClick={this.onClickBtn}
loading={this.state.loading}
disabled={this.state.isDisabled}
>
{this.state.buttonText}
</Button>
);
}
}
I've added a setTimeout of 2000ms so you can see the loading indicator.
Find the demo here: https://codesandbox.io/s/antd-reproduction-template-l1d4r
Can you check if below code solves your issue ? I Added timer to demonstrate time affect so that loading becomes evident.
With ReactJS, you don't have to use setAttribute or innerHTML as ReactJS uses virtualDOM. Even if you use them, It can get DOM in inconsistent state. As per standard React practice you should handle your requirements (i.e changing DOM) with state variables
class DashboardPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: {"btn1": false},
disabled: false,
buttonText: 'Extract'
}
onClickBtn = (btnStr) => {
let loadingCopy = Object.assign({},this.state.loading)
loadingCopy[btnStr] = true
this.setState({loading: loadingCopy})
setInterval(() => {
loadingCopy[btnStr] = false
this.setState({ loading: loadingCopy, buttonText: 'Extracted', disabled: true });
}, 3000);
}
render() {
return (
<ButtononClick={() =>this.onClickBtn("btn1")} loading={this.state.loading["btn1"]} disabled={this.state.disabled}>{this.state.buttonText}</Button>
)
}
}
import { Button } from 'antd';
import React from "react";
class SubmitButton extends React.Component {
state = {
loadings: [],
};
enterLoading = index => {
this.setState(({loadings}) => {
const newLoadings = [...loadings];
newLoadings[index] = true;
return {
loadings: newLoadings,
};
});
};
render() {
const {loadings} = this.state;
return (
<>
<Button type="primary" htmlType="submit" loading={loadings[0]} onClick={() => this.enterLoading(0)}>
Submit
</Button>
</>
);
}
Code:
https://codesandbox.io/s/withered-framework-w5lmc?file=/src/App.js

ReactJS - Pass Updated Value To Sub-Component Method

I'm working on an environment that is basically set up with a Main Component like this:
class MainComponent extends Component {
constructor(props) {
super(props);
this.state = {
selectedValues: []
};
}
render() {
const { selectedValues } = this.state;
return (
// Other components
<SubComponent selectedValues = {selectedValues} />
// Other components
);
}
}
export default MainComponent;
And a Sub Component like this:
class SubComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isExporting: false,
selectedValues: props.selectedValues
};
}
performTask = () => {
this.setState({ isWorking: true });
const { selectedValues } = this.state;
console.log(`Selected Values: ${selectedValues}`);
fetch('/api/work', {
method: 'GET'
})
.then(res => res.json())
.then((result) => {
// Handle the result
this.setState({ isWorking: false });
})
.catch((error) => {
console.log(error);
this.setState({ isWorking: false });
});
};
render() {
const { isWorking } = this.state;
return (
<Button
bsStyle="primary"
disabled={isWorking}
onClick={() => this.performTask()}
>
{isWorking ? 'Working...' : 'Work'}
</Button>
);
}
}
SubComponent.propTypes = {
selectedValues: PropTypes.arrayOf(PropTypes.string)
};
SubComponent.defaultProps = {
selectedValues: []
};
export default SubComponent;
In the Main Component, there are other components at work that can change the selectedValues. The functionality I'd like to see is that when the performTask method fires, it has the most recent and up to date list of selectedValues. With my current setup, selectedValues is always an empty list. No matter how many values actually get selected in the Main Component, the list never seems to change in the Sub Component.
Is there a simple way to do this?
I would suggest you 2 of the following methods to check this problem:
Maybe the state.selectedItems doesn't change at all. You only declare it in the contractor but the value remains, since you didn't setState with other value to it. Maybe it will work if you will refer to this.props.selectedItems instead.
Try to add the function component WillReceiveProps(newProps) to the sub component and check the value there.
If this method doesn't call, it means the selectedItems doesnt change.
Update if some of it works.
Good luck.
selectedValues in SubComponent state has not updated since it was set in SubComponent constructor. You may need to call setState again in componentWillReceivedProps in SubComponent

React: Issues with Conditional Rendering

In my React-App, i use the Firebase SDK. If a user wants to reset his password, he will be redirected to a page within my app. If the code is valid, the component <PWResetConfirmForm /> should be rended. If the code is invalid, the component <PWResetOutdatedForm /> is to be rendered.
My Page Component looks like this:
class PWResetConfirmPage extends Component {
constructor(props) {
super(props);
this.state = {};
this.verfiyResetPassword = this.verfiyResetPassword.bind(this);
}
verfiyResetPassword() {
const params = (new URL(`http://dummy.com${this.props.location.search}`)).searchParams;
const code = params.get("oobCode")
auth.doVerfiyPasswordReset(code)
.then(function () {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetConfirmForm></PWResetConfirmForm>
</div>
);
})
.catch(function () {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetOutdatedForm></PWResetOutdatedForm>
</div>
);
})
}
render() {
return (
<div>
{this.verfiyResetPassword()}
</div>
);
}
}
export default PWResetConfirmPage
When i try to run, i get a blank page and not error.
Where is my issue and how can i fix that?
Thank you very much for your help and for your time
You will not be able to return JSX from within then()/catch() of auth.doVerfiyPasswordReset() like that. You can instead approach this by taking advantage of React.Component lifecycle method componentDidMount and using setState() to manipulate state properties for conditional rendering. I've added state properties to the component, one to track whether loading (API call has completed) and one to track whether the call was a success (then) or failure (catch). These properties are used to conditionally generate JSX content for rendering. This is assuming that verfiyResetPassword() is intended to run when the component is first mounted, instead of every time render() is called:
class App extends Component {
constructor() {
super();
this.state = {
isResetVerified: null,
loading: true
};
}
componentDidMount() {
this.verfiyResetPassword();
}
verfiyResetPassword() {
const params = (new URL(`http://dummy.com${this.props.location.search}`)).searchParams;
const code = params.get("oobCode")
auth.doVerfiyPasswordReset('foobar')
.then(() => {
this.setState({
...this.state,
isResetVerified: true,
loading: false
});
})
.catch(() => {
this.setState({
...this.state,
isResetVerified: false,
loading: false
});
})
}
getContent() {
if (this.state.loading) {
return (
<div>Loading...</div>
);
} else {
if (this.state.isResetVerified) {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetConfirmForm></PWResetConfirmForm>
</div>
);
} else {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetOutdatedForm></PWResetOutdatedForm>
</div>
);
}
}
}
Here is a basic example in action.
Also, in the constructor this.verfiyResetPassword = this.verfiyResetPassword.bind(this); would only be needed if verfiyResetPassword() is executed by a DOM event such as button onClick or similar.
Hopefully that helps!
I could still fix the error myself:
class PWResetConfirmPage extends Component {
constructor(props) {
super(props);
this.state = {
isValid: false,
code: "",
};
this.verfiyResetPassword = this.verfiyResetPassword.bind(this);
}
componentDidMount() {
const params = (new URL(`http://dummy.com${this.props.location.search}`)).searchParams;
const code = params.get("oobCode")
this.setState({code:code})
auth.doVerfiyPasswordReset(code)
.then(() => {
this.setState({
...this.state,
isValid: true,
});
})
.catch(() => {
this.setState({
...this.state,
isValid: false,
});
})
}
verfiyResetPassword() {
if (this.state.isValid) {
return (
<div>
<TopBar></TopBar>
<PWResetConfirmForm code={this.state.code}></PWResetConfirmForm>
</div>
);
} else {
return (
<div>
<TopBar></TopBar>
<PWResetOutdatedForm></PWResetOutdatedForm>
</div>
);
}
}
render() {
return (
<div className="HomePage-Main">
{this.verfiyResetPassword()}
</div>
);
}
}
export default PWResetConfirmPage

How come I can't update my state?

class ProductCarousel extends Component {
constructor (props) {
super(props)
this.state = {
active: -1,
fullScreen: false
}
this.flky = {}
}
componentDidMount (props) {
this.flky = new Flickity('.carousel', flickityOptions)
this.flky.on('select', () => {
this.setState({ active: this.flky.selectedIndex })
})
}
fullScreen () {
this.setState({ fullScreen: true })
console.log(this.state)
}
render () {
const images = this.props.images
return (
<div>
<div className='carousel'>
{images.map((url, i) => (
<img
className='productPhoto'
key={i.toString()}
onClick={this.fullScreen.bind(this)}
src={stripUrl(url)}
style={{maxWidth: '100%'}}
/>
))}
</div>
</div>
)
}
}
I have this component in js, in the function fullScreen, I expect this.state.fullScreen to be true, but it's still false, why?
setState is asynchronous. Its second parameter is a callback that you can use to react to when the state was actually updated.
setState does not update the state immediately. It create a pending transition instead.
If you want to check the component's state just after setState, do it like this:
this.setState({fullScreen: true}, () => {
// I am triggered after the state is set
console.log(this.state)
})
Read about it over here

ReactJS - Elegant way to toggle state

I have a simple show / hide style that needs to toggle on a click event. Here is what I have:
constructor(props) {
super(props);
this.state = {iover: 'hide'}
}
handleClick(event) {
// this is wrong, it returns a true or false
this.setState({ iover: !this.state.iover });
// this doesn't toggle
// this.setState({ iover: this.state.iover = 'hide' ? this.state.iover = 'show' : this.state.iover ='hide' });
event.preventDefault()
}
I want to toggle this.state.iover value between 'show' & 'hide'.
What would be the most elegant way to do so.
One way to do this is to keep your state as a boolean true or false then put a ternary operator wherever you want the value "hide" or "show".
For example:
getInitialState: function() {
return {
iover: false
};
},
handleClick: function() {
this.setState({
iover: !this.state.iover
});
},
render: function(){
return (
<div className={this.state.iover ? 'show' : 'hide'}>...</div>
);
}
I think that #mark-anderson's answer is the most "elegant" way, however, the recommended way of doing a state toggling (according to React docs) is:
this.setState(prevState => ({
iover: !prevState.iover
}));
*If you need to store 'show/hide' inside that state, the code would be:
this.setState(prevState => ({
iover: prevState.iover === 'hide' ? 'show' : 'hide'
}));
Although this was a little challenge for me but I ended up like this --
class Toggle extends React.Component{
constructor(props){
super(props);
this.handleToggleVisib = this.handleToggleVisib.bind(this);
this.state = {
visib : false
}
}
handleToggleVisib(){
this.setState({ visib : !this.state.visib });
}
render(){
return(
<div>
<h1>Toggle Built</h1>
<button onClick={this.handleToggleVisib}>
{this.state.visib? 'Hide Button' : 'Show Button'}
</button>
<div>
{this.state.visib && <p>This is a tough challenege</p>}
</div>
</div>
);
}
}
ReactDOM.render(<Toggle />,document.getElementById('app'));
There's a really handy little utility for React called classnames (https://github.com/JedWatson/classnames)
It lets you conditionally render a class, which you can use to handle add the style you need for hiding/showing.
For example, here I'm toggling the state with a function:
state = {
isOpen: false
}
toggleDropdown = () => {
const toggledIsOpen = this.state.isOpen ? false : true;
this.setState({
isOpen: toggledIsOpen
});
}
Then, in the onClick handler for my dropdown , I use classnames to either print class="dropdown" or class="dropdown is-open":
// conditionally add 'is-open' class for styling purposes
const openClass = classNames("dropdown", {
"is-open": isOpen
});
return (
<div className={openClass} onClick={this.toggleDropdown}>[dropdown contents here]</div>
);
constructor(props) {
super(props);
this.state = {iover: false}
}
updateState = () {
this.setState(prevState => ({
iover: !prevState.iover
}));
}
render() {
return (
<div className={this.state.iover ? 'show' : 'hide'}>...</div>
);
}
This is the best I could come up with, was hoping for something shorter:
handleClick(event) {
let show = this.state.iover;
let index = show.indexOf('show');
if (index != -1) {
show = 'hide';
} else {
show = 'show';
}
this.setState({ iover: show });
event.preventDefault()
}

Resources