React setState doesn't update the state - javascript

I am currently using react-modal in my project and i have problem opening and closing it probably from other component.
class MainComponent {
constructor() {
this.state = {reportOpen: false};
}
closeReport = (e) => {
this.setState({reportOpen: false}, () =>
console.log(this.state.reportOpen)); // This line print true !!!
}
render() {
return (
<Button onClick={(e) => this.setState({reportOpen: true})}/>
<ReportModal isOpen={this.state.reportOpen} onClose= .
{this.closeReport}/>
)
}
}
// Modal
class ReportModal {
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.isOpen !== prevState.isOpen) {
return ({isOpen: nextProps.isOpen});
}
else {
return null;
}
}
render() {
return <Modal isOpen={this.state.isOpen}
onRequestClose={this.props.onClose}
shouldCloseOnOverlayClick={true}
shouldCloseOnEsc={true}/>
}
}
Due to the mentioned problem, I couldn't close the modal once i opened it. Please help me to figure out the problem here. Thanks for any help.

Missing extends React.Component in class declaration.
Missing super(props); call in constructor.

Please debug at getDerivedStateFromProps for the new derived state.
Also why don't you just handle the same in ReportModal component, the callback seems overwork

Related

React axios request in ComponentDidMount not passing data as props to component

Hi guys I can't see my Error here hope someone can hlep...
This is my fetch Data class:
export default class Auftrag extends Component {
state = {
auftraege: "Test",
};
getAuftraege = () => {
axios.get("Auftraege/auftraege").then(e => {
this.setState({
auftraege: e.data,
});
console.log(e.data);
});
};
componentDidMount() {
this.getAuftraege();
}
render() {
return (
<>
<AuftragDisplay test={this.state.auftraege} ></AuftragDisplay>
</>
);
}
}
And this is my constructor in my Display class:
constructor(props) {
super(props);
console.log(props);
}
The axios Request is getting fired and I get the right data in my console. But It is not getting passed to my Component.
Hope someone knows whats wrong and can help me
SOLVED:
Thx to san I tried it and could solve the problem. I got the data passed but console.log() was called before the update so I got the old data. THX again
Your code looks fine. you can see below same code with different api as an example
class Auftrag extends Component {
state = {
auftraege: "Test",
};
getAuftraege = () => {
axios
.get("https://jsonplaceholder.typicode.com/posts/1")
.then(e => this.setState({auftraege: e.data}))
};
componentDidMount() {
this.getAuftraege();
}
render() {
return (
<>
<AuftragDisplay test={this.state.auftraege} ></AuftragDisplay>
</>
);
}
}
const AuftragDisplay = ({test}) =><h2>Hi--->{test.title}</h2>
Just put the state inside constructor of Auftrag class, I should work.

React: Calling setState within render method throws error

As the title suggests, only after the first message received in my chat-window - this initial message is retrieved from a GET request so it's not synchronous - I want to show/render a button. At the moment it throws an error saying I cant set the state within the render method.
I also tried the show logic in the button class as well as the 'parent' class which is my messagelist which I'm putting the button in its render method.
There is this.props.messages which is an array of the messages and so is 'messages'. this.props.messages[0].data.text is the first message, although it does console many times each messsage in the dev tools when i try console it, and of course it throws the setState error when i try to show the button.
I have a simple button class:
class Button extends Component {
render() {
return (
<div>
{<button>Return</button >}
</div>
)
}
}
export default Button;
and my messageList class, where I have the this.props.messages which is an array of the messages, this.props.messages[0] is the first message , and message..which console's every single message if i console.log it.
If i write either (if message.data.text OR this.props.messages[0] === 'my first string') { console.log ('..... '}then it always counts as true and consoles and the setstate goes into a loop.
import Message from './Messages'
import Button from './Button'
class MessageList extends Component {
constructor(props) {
super(props);
this.state = {
showing: false,
};
this.showButton = this.showButton.bind(this);
}
showButton() {
const { showing } = this.state;
this.setState({
// toggle value of `showing`
showing: !showing,
});
}
componentDidUpdate(prevProps, prevState) {
this.scrollList.scrollTop = this.scrollList.scrollHeight;
}
onlyInitialMessage(message) {
if (this.props.messages[0].data.text = `Hi I'm Joe your store assistant, I'm here to help. Here's what I can do: Answer questions on store policies, process a return or just general inquiries.`) {
this.showButton();
}
}
// way to render a function.
// {this.renderIcon()}
render() {
return (
<div className="sc-message-list" ref={el => this.scrollList = el}>
{this.props.messages.map((message, i) => {
{ this.onlyInitialMessage() }
return <Message message={message} key={i} />
})}
{this.state.showing && <Button />}
</div>)
}
}
I'm not sure If I have my logic in the wrong place here? I tried to move it around lots of times, I am new to React!
Firstly, The issue is that you are setting state in the render method indirectly by calling { this.onlyInitialMessage() } in render.
Secondly, your if condition is not comparing value but assinging value which will always return true
if (this.props.messages[0].data.text === `Hi I'm Joe your store assistant, I'm here to help. Here's what I can do: Answer questions on store policies, process a return or just general inquiries.`) {
To solve it, you must call onlyInitialMessage within componentDidMount
import Message from './Messages'
import Button from './Button'
class MessageList extends Component {
constructor(props) {
super(props);
this.state = {
showing: false,
};
this.showButton = this.showButton.bind(this);
}
componentDidMount() {
this.onlyInitialMessage();
}
showButton() {
const { showing } = this.state;
this.setState({
// toggle value of `showing`
showing: !showing,
});
}
componentDidUpdate(prevProps, prevState) {
this.scrollList.scrollTop = this.scrollList.scrollHeight;
}
onlyInitialMessage(message) {
if (this.props.messages[0].data.text == `Hi I'm Joe your store assistant, I'm here to help. Here's what I can do: Answer questions on store policies, process a return or just general inquiries.`) {
this.showButton();
}
}
// way to render a function.
// {this.renderIcon()}
render() {
return (
<div className="sc-message-list" ref={el => this.scrollList = el}>
{this.props.messages.map((message, i) => {
return <Message message={message} key={i} />
})}
{this.state.showing && <Button />}
</div>)
}
}

Refactoring UNSAFE_componentWillReceiveProps

I have an IFrameComponent component, inspired by this post.
It looks basically like this :
class IFrameComponent extends React.Component {
shouldComponentUpdate() {
return false;
}
componentWillReceiveProps(nextProps) {
if(this.props.content !== nextProps.content) {
const html = getHTMLFromContent();
const fdoc = this.iFrame.contentDocument;
fdoc.write(html);
}
}
render() {
return (<iframe sandbox="..." ref={f => this.iFrame = f} />);
}
}
Now that componentWillReceiveProps is considered unsafe, I'm trying to get rid of it.
The ways React advices to refactor componentWillReceiveProps are basically either to use static getDerivedStateFromProps or componentDidUpdate
Sadly, componentDidUpdate will never get called, because shouldComponentUpdate returns false (and I think this is fine ?) and I wouldn't be able to access this.iFrame reference in the static method getDerivedStateFromProps.
How would one refactor this code ?
I think, One possible way is:
let iFrameRefs = {}
class IFrameComponent extends React.Component {
static getDerivedStateFromProps (props) {
if (iFrameRefs[props.id]) {
const html = getHTMLFromContent();
const fdoc = iFrameRefs[props.id].contentDocument;
fdoc.write(html);
}
return null
}
shouldComponentUpdate() {
return false;
}
render() {
return (<iframe sandbox="..." ref={f => iFrameRefs[this.props.id] = f} />);
}
}
Now from Parent Component pass unique id to each component. You can also manage id in IFrameComponent.
<IFrameComponent id='1' />
<IFrameComponent id='2' />

React "Cannot read property 'bind' of undefined"

I looked at many other answers but I couldn't figure it out. Here is my code:
// userInputActions.js
...
export function dummy() {
console.log('dummy function called');
}
...
// *userInputPage.js*
import * as userInputActions from '../actions/userInputActions';
class UserInput extends React.Component {
constructor(props) {
super(props);
this.state = {
};
// When un-commented it shows '*f dummy()*' is imported
// console.log('actions: ', userInputActions);
this.dummy = this.dummy.bind(this);
}
render () {
return (
<div className="container-fluid align-items-center">
<FieldLevelValidationForm onSubmit={this.dummy}/>
</div>
);
}
}
const mapStateToProps = (state) => ({
});
const mapDispatchToProps = (dispatch) =>
bindActionCreators(
{
...userInputActions
}, dispatch
);
export default connect(mapStateToProps, mapDispatchToProps)(UserInput);
Note 'FieldLevelValidationForm' is a redux-form and onSubmit is one of the form function arguments.
I tried various things and the bind function does not work. Can someone please let me know where am I going wrong. I think it has something to do with the render() function and the lifetime of the component but I do not know enough yet.
Edit
Thank you - learned a lot from all answers. All of them work. I wish I could give more than one check-mark. However, I think the most appropriate use for my case is to call it as a prop and dispatch an action as so on.
The dummy function is passed as props, so you should access it with this.props.dummy in your render() instead.
There's also no need to bind it to this as it's not using the this instance.
The dummy function you are trying to bind is not in this class. So it would just be:
this.dummy = userInputActions.dummy.bind(this);
Function definition of dummy is missing.
constructor(props) {
super(props);
this.state = { };
this.dummy = this.dummy.bind(this);
}
dummy(e) {
// form submission action
userInputActions.dummy(); // maybe
}
render () {
return (
<div className="container-fluid align-items-center">
<FieldLevelValidationForm onSubmit={this.dummy}/>
</div>
);
}

React.js add a class on socket.on

I want to add a class when socket.on is invoked. So that a widget opens automatically on a specific socket.id.
The intention is that socket.on('startChat', function() { }); adds so that the Chat.js file starts running.
socket.on('startChat', function() {
});
My code:
Website.js
constructor(props) {
super(props);
this.state = {
showComponent: false,
};
socket.on('startChat', function() {
});
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(function(prevState) {
return {
isToggleOn: !prevState.isToggleOn
};
});
}
render() {
return ( <
div >
<
button onClick = {
this.handleClick
} > Test < /button> {
this.state.isToggleOn ?
<
Chat / > :
null
} <
/div>
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
You can use your state.showComponent to conditionally render() a component.
It is convenient to attach any handler or listener into the componentDidMount() method.
Following the code you pasted:
Update
It looks this is not bind even if using ES6 arrow notation. Make sure the constructor is present, and try to bind it back.
Added react class and imports.
import React, {Component} from 'react'
class Website extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
isToggleOn: false // I added this
};
// This binding is necessary to make `this` work in the callback
// No if you use the current notation ()=>{}
// this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
socket.on('startChat', function() {
console.log("Chart started!!!")
});
}
handleClick() {
let state=this.state
state.isToggleOn = !this.state.isToggleOn
this.setState(state);
}
render() {
return (
<div>
<button onClick={this.handleClick().bind(this)}> Test </button>
{ this.state.isToggleOn ? <Chat / > : null }
</div>
)
}
}
You have to add socket listener to React useEffect() in functional component.
Or if you are using React class component then you should insert socket listener to componentDidUpdate function

Categories

Resources