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
Related
React
i have tried to make a button which will show how many times it is clicked using react state. but i want to do this with many component. So i wrote my state and update function with setState in a parent Component and want to pass the state as props, but the problem is as i pass the state once, then after the state is updated (when the button is clicked) the props dont update with that. and i cant see how many times the button is clicked.
class App extends Component {
constructor(props) {
super(props);
this.state = {
count: 2,
};
}
clickCount() {
this.setState((prev) => {
return { count: prev.count + 1 };
})
}
render() {
return (
<div>
<MainContent
handler={this.clickCount} totalCount={this.state.count}/>
</div>
);
}
}
export default App;
You seemed to have missed binding the function to this
class App extends Component {
constructor(props) {
super(props);
this.state = {
count: 2,
};
this.clickCount = this.clickCount.bind(this); // you may have missed this..
}
clickCount() {
this.setState((prev) => {
return { count: prev.count + 1 };
})
}
render() {
return (
<div>
<MainContent handler={this.clickCount} totalCount={this.state.count}/>
</div>
);
}
}
export default App;
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
I have an array of objects inside my class that I am modifying and only when a keypress happens do I want to render this object visually.
class Example extends React.Component {
constructor(props) {
super(props);
this.myArr = []; // this is an array of objects
}
render() {
return (
???
);
}
}
Now I modify the contents of this.myArr in many different methods. And only when I'm ready (on a keypress or some other event) do I want to render it.
Now in my render() should I have a reference to this.myArr and then use this.forceUpdate() when I want to force a re-render.
Or should I move myArr into this.state.myArr, and modify this.state.myArr in my methods and when I am ready to display it, in my render() reference to this.state.myArr, and somehow force a rerender with this.setState(myArr: this.state.myArr);
***Second Update - I think this may be what you want. Obviously, you'll need to add a lot of logic for your mouse click events. It should point you in the right direction though.
class Example extends React.Component {
constructor(props) {
super(props);
this.myArr = [];
this.state = {
myArr: [{ width: 10 }, { width: 20 }], // header widths
};
}
// call changeHeaders when needed
// it will update state, which will cause a re-render
changeHeaders = (column, newWidth) => {
const newArr = [...this.state.myArr];
if (newArr[column]) {
newArr[column].width = newWidth;
}
this.setState({ myArr: newArr });
}
renderArray = () => {
return this.state.myArr.map(({ width }) => <div>{width}</div>);
}
render() {
return (
<div>
{this.renderArray()}
</div>
);
}
}
Either way would work but I think its better practice to use this.state to hold your array and use this.setState() to force a re-render and call the this.setState within your keypress event callback
Here is how you update your array value -
Correct modification of state arrays in ReactJS
There's a very good explanation why you should avoid using this.forceUpdate() in here answered by Chris.
In this case you should use state. State is intended to be used for any data that affect how your component looks. Remember that you may not modify your state directly, it's an antipattern. Instead, you should create a copy of the array and update the state with that modified copy. Like so:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {myArr: []}; // this is an array of objects
}
mutateArraySomehow() {
const nextArr = [...this.state.myArr];
nextArr.push('heyyoooo');
this.setState({myArr: nextArr});
}
render() {
return (
???
);
}
}
This is how i would have done it
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
myArr: [],
display: false
}
}
render() {
if(this.state.display) {
return (
<MyComponent onKeyPress=(ev=>{
this.setState({display: true})
})
/>
);
} else {
return (<div></div>)
}
}
}
When there is a modification to the array elements, you need to do a setState of status to true.This would perform the conditional rendering and will display the modified array.
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
myArr = []; // this is an array of objects
status:false
}
}
modifyArr = () => {
//some array modifications
this.setState({status:true})
}
render() {
return (
{this.state.status ? null : (<div>`Display your array here`</div>)}
);
}
}
In short, define state inside class like:
state: {
firstName: String
} = {
firstName: ''
}
And inside render function you would do this:
this.setState({ firstName: 'Junior' })
I have a React component and I want to toggle a css class when clicked.
So I have this:
export class myComponent extends React.Component {
constructor() {
super();
this.state = { clicked: false };
this.handleClick = this.handleClick.bind(this);
}
render() {
return (
<div>
<div onClick={this.clicked}><span ref="btn" className="glyphicon"> </span></div>
</div>
);
}
handleClick() {
this.refs.btn.classList.toggle('active');
}
componentDidMount() {
this.refs.btn.addEventListener('click', this.handleClick);
this.setState({
clicked: this.state.clicked = true,
});
}
componentWillUnmount() {
this.refs.btn.removeEventListener('click', this.handleClick);
this.setState({
clicked: this.state.clicked = false,
});
}
}
This problem is that ESLint keeps telling me "this.refs" is depreciated.
What do I do instead? How can I fix it so it's not using depreciated code?
The Lint rule you are referring to is called no-string-refs and warns you with:
"Using string literals in ref attributes is deprecated (react/no-string-refs)"
You are getting this warning because have implemented the deprecated way of using refs (by using strings). Depending on your React version, you can do:
React 16.3 and later
constructor() {
super();
this.btnRef= React.createRef();
this.state = { clicked: false };
this.handleClick = this.handleClick.bind(this);
}
render() {
return (
<div>
<div onClick={this.addVote}><span ref={this.btnRef} className="glyphicon"> </span></div>
</div>
);
}
React 16.2 and older
constructor() {
super();
this.btnRef; //not necessary to declare the variable here, but I like to make it more visible.
this.state = { clicked: false };
this.handleClick = this.handleClick.bind(this);
}
render() {
return (
<div>
<div onClick={this.addVote}><span ref={(el) => this.btnRef = el} className="glyphicon"> </span></div>
</div>
);
}
For even better readability, you could also do:
render() {
let myRef = (el) => this.btnRef = el;
return (
<div>
<div onClick={this.addVote}><span ref={myRef} className="glyphicon"> </span></div>
</div>
);
}
Have a look at what the official documentation says on Refs and the DOM, and this section in particular:
Legacy API: String Refs
If you worked with React before, you might be
familiar with an older API where the ref attribute is a string, like
"textInput", and the DOM node is accessed as this.refs.textInput. We
advise against it because string refs have some issues, are considered
legacy, and are likely to be removed in one of the future releases. If
you're currently using this.refs.textInput to access refs, we
recommend the callback pattern instead.
The reason this ESLint rule exists is that string Refs are on their way out. However, for the code above I would recommend to not use a Ref in the first place.
Don't Overuse Refs
React's advantage is that it is declarative. Meaning, we have state and an expression (returned JSX) of how the UI (more precisely the DOM) should look given a certain state.
Whatever can be done using just state and UI expression, should be done this way. The problem with the use of a Ref in the code above is that it makes the code imperative. We can't understand how the DOM will look just from the JSX. Here is how you could achieve the same result in a declarative way:
export class myComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false
};
}
handleClick = () => { // with arrow function there is no need for binding.
this.setState(
prevState => {
return {
active: !prevState.active
}
}
)
}
render() {
return (
<div>
<span
onClick={this.handleClick}
className={`glyphicon ${this.state.active && "active"}`}
>
Hello World
</span>
</div>
);
}
}
Refs should be used when state and UI expression aren't enough, and you need access to the actual DOM. For example, focusing on an input field, scrolling to an element, or getting the exact width and height of an element.
If you do use Refs, avoid string refs
String refs harm performance, aren't composable, and are on there way out.
string refs have some issues, are considered legacy, and are likely to
be removed in one of the future releases. [Official React documentation]
[resource1][1], [resource2][1]
Option #1: Use React.createRef
class MyComponent extends Component {
constructor(props) {
super(props)
this.myRef = React.createRef() // create a ref object
}
render() {
return <div ref={this.myRef}></div> // Attach the ref property to a dom element
}
}
Option #2: Use a ref callback
class MyComponent extends Component {
constructor(props){ // Optional, declare a class field
super(props)
this.myRef=null
}
render() {
return <div ref={ (ref) => this.myRef=ref }></div>
} // Attach the dom element to a class field
}
you can try a more declarative way. I changed your code to reflect this. You just need to remind that a component will refresh and call render in every state/props change. So, we can create the class of your element inside render method.
import React from 'react'
export default class myComponent extends React.Component {
constructor() {
super();
this.state = { clicked: false };
this.handleClick = this.handleClick.bind(this);
}
render() {
let btnClass = 'glyphicon'
if(this.state.clicked){
btnClass+=' active'
}
return (
<div>
<div onClick={this.handleClick}><span ref="btn" className={btnClass}> </span></div>
</div>
);
}
handleClick() {
this.setState({
clicked: !this.state.clicked
})
}
}
Still trying to learn React. I'm trying to show an image when you hover. This is my Item component.
import React from 'react';
import Eyecon from '../../static/eye.svg';
class Item extends React.Component {
constructor(props) {
super(props);
this.displayName = 'Item';
this.state = {
hover: false
};
}
mouseOver() {
this.setState({hover: true});
}
mouseOut() {
this.setState({hover: false});
}
render() {
const { item, i } = this.props;
return (
<div className="grid-box" onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>
{this.state.hover ? (<img src={Eyecon}/>) : null}
</div>
)
}
}
export default Item;
How would I make it so only the item I hover over shows the image?
This is just a 'this' binding issue. Put a console.log inside of your mouseOver and mouseOut methods and you'll notice that your state isn't changing.
There are many ways to bind the 'this' context in your class methods. I'll show you three ways to do it in this example (DO NOT do all three methods, just choose one).
import React from 'react';
import Eyecon from '../../static/eye.svg';
class Item extends React.Component {
constructor(props) {
super(props);
this.displayName = 'Item';
// 1. bind your functions in the constructor.
this.mouseOver = this.mouseOver.bind(this);
this.mouseOut = this.mouseOut.bind(this);
this.state = {
hover: false
};
}
// 2. bind it with fat arrows.
mouseOver = () => {
this.setState({hover: true});
}
mouseOut() {
this.setState({hover: false});
}
render() {
const { item, i } = this.props;
// 3. bind them in the render method (not recommended for performance reasons)
return (
<div className="grid-box" onMouseOver={this.mouseOver.bind(this)} onMouseOut={this.mouseOut.bind(this)}>
{this.state.hover ? (<img src={Eyecon}/>) : null}
</div>
)
}
}
export default Item;
Here's an explanation of different ways to bind your 'this' context in react using ES6 classes:
http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html
Maybe it's because you have to bind mouseOver and mouseOut calls in order to use this.setState inside them.
Replace:
<div className="grid-box" onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>
with:
<div className="grid-box" onMouseOver={this.mouseOver.bind(this)} onMouseOut={this.mouseOut.bind(this)}>
The other solutions suggested are perfectly valid, however you can solve this easily by just converting your functions to ES6 arrow functions.
An arrow function expression has a shorter syntax compared to function expressions and lexically binds the this value (does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous.
Like so:
mouseOver = () => {
this.setState({hover: true});
}
mouseOut = () => {
this.setState({hover: false});
}
Simple.