How do I get this functionality using React?
function myFunction() {
var node = document.createElement("div");
var textnode = document.createTextNode("Button was clicked.");
node.appendChild(textnode);
document.getElementById("root").appendChild(node);
}
in your render function write the div you want and make a state
then change a state to true to the div to be shown
somthing like this
constructor(props) {
super(props)
this.state = {
showDiv: false
}
}
render (){
return (
{
this.state.showDiv ? your div : <React.Fragment />
}
)
}
changeDivDisplay(){
this.setState({showDiv:true})
}
also i do recommend you to read about the state and the refs of react
Related
I would like to implement something like this using React hooks:
const header = document.querySelector(".nav-header");
function stickyHeader() {
if (window.pageYOffset > 600) {
header.classList.remove("header");
} else {
header.classList.add("header");
}
}
window.addEventListener("scroll", () => {
stickyHeader();
});
Above, is how I would have manipulated the DOM in vanilla js. I would like to do the same for a component in react.
Possibly try a getClassName method which returns a joined array of classes on every render
For a functional component
const getClassName = () => {
let classes = ["nav"];
if (window.pageYOffset > 600) {
classes.push("header");
}
//more conditions if required
return classes.join(" ");
//returns "nav header" || "nav"
}
and then in your component return method
return (
<div className={getClassName()}><div>
)
className is an element attribute. Therefore you need to set it in your render component function like below:
...
render() {
let className = 'menu';
if (this.props.isActive) {
className += ' menu-active';
}
return <span className={className}>Menu</span>
}
here is an example on how to handle scroll events with React
At first run the program is working correctly.
But after clicking on the sum or minus button the function will not run.
componentDidMount() {
if(CONST.INTRO) {
this.showIntro(); // show popup with next and prev btn
let plus = document.querySelector('.introStep-plus');
let minus = document.querySelector('.introStep-minus');
if (plus || minus) {
plus.addEventListener('click', () => {
let next = document.querySelector(CONST.INTRO[this.state.introStep].element);
if (next) {
next.parentNode.removeChild(next);
}
this.setState({
introStep: this.state.introStep + 1
});
this.showIntro();
});
}
}
As referenced in React documentation: refs and the dom the proper way to reference DOM elements is by using react.creatRef() method, and even with this the documentation suggest to not over use it:
Avoid using refs for anything that can be done declaratively.
I suggest that you manage your .introStep-plus and introStep-minus DOM element in your react components render method, with conditional rendering, and maintain a state to show and hide .introStep-plus DOM element instead of removing it inside native javascript addEventListener click
Here is a suggested modification, it might not work directly since you didn't provide more context and how you implemented the whole component.
constructor() {
...
this.state = {
...
showNext: 1,
...
}
...
this.handlePlusClick = this.handlePlusClick.bind(this);
}
componentDidMount() {
if(CONST.INTRO) {
this.showIntro(); // show popup with next and prev btn
}
}
handlePlusClick(e) {
this.setState({
introStep: this.state.introStep + 1,
showNext: 0,
});
this.showIntro();
});
render() {
...
<div className="introStep-plus" onClick={this.handlePlusClick}></div>
<div className="introStep-minus"></div>
{(this.stat.showNext) ? (<div> NEXT </div>) : <React.fragment />}
...
}
I have a prop called transcript inside one of my components. It gets updated when I speak a voice intent. I would like to run a function anytime it changes which takes in the transcript as an argument
Here I am trying to do an OnChange={} function inside the span which I have loaded the transcript into but I know this method won't work, it was simply the easiest way of explaining what I wanted to accomplish
import React, { Component } from "react";
import SpeechRecognition from "react-speech-recognition";
import { Button } from 'react-bootstrap';
class Dictaphone extends Component {
highlightOnRead=(transcript, cursor)=>{
console.log("transcript",transcript)
console.log("cursor",cursor.anchorNode.parentNode.id) //we only need the word
if(transcript.includes(" ")){
transcript = transcript.split(" ").pop()
}
if(transcript = cursor.textContent.toLowerCase()){
cursor.style.backgroundColor = 'yellow'; //highlight the span matching the intent
}
cursor = document.getElementById(cursor.anchorNode.parentNode.id).nextSibling.nextElementSibling;
return cursor
};
render() {
const {transcript, resetTranscript, browserSupportsSpeechRecognition} = this.props
var cursor=''
if (!browserSupportsSpeechRecognition) {
return null
}
if(transcript==="notes"||transcript==="cards"||transcript==="home"|| transcript==="settings" || transcript==="read document"){
this.libraryOfIntents(transcript,resetTranscript);
}
return (
<div>
<span id="transcriptSpan"className="transcriptspan" onChange={()=>{
if(this.props.readAlongHighlightState===true){
if(cursor===''){
this.highlightOnRead(transcript,window.getSelection())
}else{
this.highlightOnRead(transcript,cursor)
}
}
}}> {transcript} </span>
<Button variant="outline-dark" onClick={resetTranscript}>Reset</Button>
</div>
)
}
}
export default SpeechRecognition(Dictaphone)
You can use lifecycle method called componentDidUpdate
componentDidUpdate(prevProps) {
if (this.props.transcript !== prevProps.transcript) { // check if your props is changed
// Make your function call here
}
}
I am trying to add an onClick event handler to objects in an array where the class of a clicked object is changed, but instead of only changing one element's class, it changes the classes of all the elements.
How can I get the function to work on only one section element at a time?
class Tiles extends React.Component {
constructor(props) {
super(props);
this.state = {
clicked: false,
content : []
};
this.onClicked = this.onClicked.bind(this);
componentDidMount() {
let url = '';
let request = new Request(url, {
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json'
})
});
fetch(request)
.then(response => response.json())
.then(data => {
this.setState({
content : data
})
} );
}
onClicked() {
this.setState({
clicked: !this.state.clicked
});
}
render() {
let tileClass = 'tile-content';
if (this.state.clicked) {
tileClass = tileClass + ' active'
}
return (
<div className = 'main-content'>
{this.state.pages.map((item) =>
<section key = {item.id} className = {tileClass} onClick = {this.onClicked}>
<h4>{item.description}</h4>
</section>)}
<br />
</div>
)
}
class App extends React.Component {
render() {
return (
<div>
<Tiles />
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('content-app'))
You have onClicked() define in your 'main-content' class. So that's where it fires.
constructor(props) {
// super, etc., code
this.onClicked = this.onClicked.bind(this); // Remove this line.
}
Remove that part.
You can keep the onClicked() function where it is. Your call in render() is incorrect, though: onClick = {this.onClicked}>. That accesses the onClicked ATTRIBUTE, not the onClicked FUNCTION, it should be this.onClicked().
Let me cleanup your call in render() a little bit:
render() {
let tileClass = 'tile-content';
return (
<div className = 'main-content'>
// some stuff
<section
key={item.id}
className={tileClass}
onClick={() => this.onClicked()} // Don't call bind here.
>
<h4>{item.description}</h4>
</section>
// some other stuff
</div>
)
}
It is happening for you, because you are assigning active class to all sections once user clicked on one of them. You need somehow to remember where user clicked. So I suggest you to use array, where you will store indexes of all clicked sections. In this case your state.clicked is an array now.
onClicked(number) {
let clicked = Object.assign([], this.state.clicked);
let index = clicked.indexOf(number);
if(index !== -1) clicked.splice(index, 1);
else clicked.push(number)
this.setState({
clicked: clicked
});
}
render() {
let tileClass = 'tile-content';
return (
<div className = 'main-content'>
{this.state.pages.map((item, i) => {
let tileClass = 'tile-content';
if(this.state.clicked.includes(i)) tile-content += ' active';
return (
<section key = {item.id} className = {tileClass} onClick = {this.onClicked.bind(this, i)}>
<h4>{item.description}</h4>
</section>
)
})}
<br />
</div>
)
}
StackOverflow does a particularly poor job of code in comments, so here's the implementation of onClicked from #Taras Danylyuk using the callback version of setState to avoid timing issues:
onClicked(number) {
this.setState((oldState) => {
let clicked = Object.assign([], this.state.clicked);
let index = clicked.indexOf(number);
if(index !== -1) {
clicked.splice(index, 1);
} else {
clicked.push(number);
}
return { clicked };
});
}
The reason you need this is because you are modifying your new state based on the old state. React doesn't guarantee your state is synchronously updated, and so you need to use a callback function to make that guarantee.
state.pages need to keep track of the individual click states, rather than an instance-wide clicked state
your onClick handler should accept an index, clone state.pages and splice your new page state where the outdated one used to be
you can also add data-index to your element, then check onClick (e) { e.currentTarget.dataset.index } to know which page needs to toggle clickstate
I have a React component that manage multiple accordion in a list, but when i update a children, on React dev tools, it was showing the updated text but on the view/ui, it doesnt update. Please advice.
var AccordionComponent = React.createClass({
getInitialState: function() {
var self = this;
var accordions = this.props.children.map(function(accordion, i) {
return clone(accordion, {
onClick: self.handleClick,
key: i
});
});
return {
accordions: accordions
}
},
handleClick: function(i) {
var accordions = this.state.accordions;
accordions = accordions.map(function(accordion) {
if (!accordion.props.open && accordion.props.index === i) {
accordion.props.open = true;
} else {
accordion.props.open = false;
}
return accordion;
});
this.setState({
accordions: accordions
});
},
componentWillReceiveProps: function(nextProps) {
var accordions = this.state.accordions.map(function(accordion, i) {
var newProp = nextProps.children[i].props;
accordion.props = assign(accordion.props, {
title: newProp.title,
children: newProp.children
});
return accordion;
});
this.setState({
accordions: accordions
});
},
render: function() {
return (
<div>
{this.state.accordions}
</div>
);
}
Edit:
The component did trigger componentWillReceiveProps event, but it still never get updated.
Thanks
After days of trying to solve this, I have came out with a solution. componentWillReceiveProps event was trigger when the component's props changes, so there was no problem on that. The problem was that the childrens of the AccordionListComponent, AccordionComponent was not updating its values/props/state.
Current solution:
componentWillReceiveProps: function(nextProps) {
var accordions = this.state.accordions.map(function(accordion, i) {
// get current accordion key, and props value
// update new props with old
var newAc = clone(accordion, nextProps.children[i].props);
// update current accordion with new props
return React.addons.update(accordion, {
$set: newAc
});
});
this.setState({
accordions: accordions
});
}
I have tried to do only clone and reset the accordions, but apparently it did not update the component.
Thanks
I encountered a similar issue. So it may be relevant to report my solution here.
I had a graph that wasn't getting updated any time I hit just once the load data button (I could see visual changing after the second click).
The graph component was designed as a child of a parent component (connected to the Redux store) and data were passing therefore as a prop.
Issue: data were properly fetched from the store (in parent) but it seems the graph component (child) wasn't reacting to them.
Solution:
componentWillReceiveProps(nextProps) {
this.props = null;
this.props = nextProps;
// call any method here
}
Hope that might contribute in someway.
I usually handle this issue with below technique, its the solution if you are home-alone ;)
var _that = this;
this.setState({
accordions: new Array()
});
setTimeout(function(){
_that.setState({
accordions: accordions
});
},5);