I'm trying to create a counter and I can't get what's in the render to show on the page.
Here is my code in JS from Codepen (with React and ReactDOM added in external JS)
class Counter extends React.Component {
constructor() {
super();
this.state = {
};
this.flash = "";
this.count = 0;
}
componentWillReceiveProps(nextProps) {
var newValue = nextProps.count;
if (this.count !== newValue) {
this.flash = this.flash === "flash1" ? "flash2" : "flash1";
}
}
render () {
return (
<div id="counter">
<button>+</button>
<button>-</button>
<div id="count" className={this.flash}>
{this.count}
</div>
</div>
);
};
}
ReactDOM.render(<Counter />, document.getElementById('countContainer'));
I can see in my JS code that normally the should be the color brown in my Codepen, so I'm obviously missing something (it's currently yellow).
In my HTML I have the following
<div id="countContainer">
</div>
Note: I'm not done with the code in regards to what it should be able to do in the end. I figured I should try and get it to render on the page before I continue.
My codepen is: URL
I tried this and got an error at the line where the first JSX tag is inside render(). I fixed it by adding the Babel preprocessor in addition to React and ReactDOM.
You can try this code to get the counter working with both increment and decrement count .
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count :0
};
this.increment=this.increment.bind(this);
this.decrement=this.decrement.bind(this);
}
increment(){
this.setState({count:this.state.count+1});
}
decrement(){
this.setState({count:this.state.count-1});
}
render () {
return (
<div id="counter">
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<div>
{this.state.count}
</div>
</div>
);
}
}
ReactDOM.render(<Counter />, document.getElementById('countContainer'));
just define the functions and trigger them using onClick().
Here is the working counter in codepen with your code, modified it a bit to make it work. https://codepen.io/coderakki/pen/QOQqMP?editors=0010
Update : also, you need to define count within the state of your component .
Related
I am building a simple react app for learning purpose, I just started learning react-js, I was trying to add paragraph dynamically on user action and it worked perfectly But I want to add an onClick event in insertAdjacentHTML (basically innerHTML).
But onclick event is not working in innerHTML
app.js
const addParagraph = () => {
var paragraphSpace = document.getElementById('container')
paragraphSpace.insertAdjacentHTML('beforeend', `<p>I am dynamically created paragraph for showing purpose<p> <span id="delete-para" onClick={deleteParagraph(this)}>Delete</span>`
}
const deleteParagraph = (e) => {
document.querySelector(e).parent('div').remove();
}
class App extends React.Component {
render() {
return (
<div>
<div onClick={addParagraph}>
Click here to Add Paragraph
</div>
<div id="container"></div>
</div>
)
}
}
What I am trying to do ?
User will be able to add multiple paragraphs and I am trying to add a delete button on every paragraph so user can delete particular paragraph
I have also tried with eventListener like :-
const deleteParagraph = () => {
document.querySelector('#delete').addEventListener("click", "#delete",
function(e) {
e.preventDefault();
document.querySelector(this).parent('div').remove();
})
}
But It said
deleteParagraph is not defined
I also tried to wrap deleteParagraph in componentDidMount() But it removes everything from the window.
Any help would be much Appreciated. Thank You.
Do not manipulate the DOM directly, let React handle DOM changes instead. Here's one way to implement it properly.
class App extends React.Component {
state = { paragraphs: [] };
addParagraph = () => {
// do not mutate the state directly, make a clone
const newParagraphs = this.state.paragraphs.slice(0);
// and mutate the clone, add a new paragraph
newParagraphs.push('I am dynamically created paragraph for showing purpose');
// then update the paragraphs in the state
this.setState({ paragraphs: newParagraphs });
};
deleteParagraph = (index) => () => {
// do not mutate the state directly, make a clone
const newParagraphs = this.state.paragraphs.slice(0);
// and mutate the clone, delete the current paragraph
newParagraphs.splice(index, 1);
// then update the paragraphs in the state
this.setState({ paragraphs: newParagraphs });
};
render() {
return (
<div>
<div onClick={this.addParagraph}>Click here to Add Paragraph</div>
<div id="container">
{this.state.paragraphs.map((paragraph, index) => (
<>
<p>{paragraph}</p>
<span onClick={this.deleteParagraph(index)}>Delete</span>
</>
))}
</div>
</div>
);
}
}
insertAdjecentHTML should not be used in javascripts frameworks because they work on entirely different paradigm. React components are rerendered every time you change a component state.
So you want to manipulate look of your component by changing its state
Solution:
In constructor initialize your component's state which you will change later on button click. Initial state is array of empty paragraphs.
constructor() {
super()
this.state = {
paragraphs:[]
}
}
And alter that state on button click - like this:
<div onClick={addParagraph}>
Add Paragraph function
const addParagraph = () =>{
this.state = this.state.push('New paragraph')
}
Rendering paragraphs
<div id="container">
this.state.paragraphs.map(paragraph =>{
<p>{paragraph}</p>
})
</div>
Additional tip for ReactJS in 2022 - use Functional components instead of Class components
I was trying to debug a sidebar for a project I'm making. One of the sidebar buttons don't trigger the onClick event I specified. I was looking 30 minutes into the code, even went as far as to duplicate it and remove any unneccessary elements in order to debug it. My conclusion was:
One of the buttons that I already had works properly and the other
one I'm trying to fix doesn't even though they share the exact same
code.
Why is it behaving that way, is beyond me.
Can you find out why?
This is the cursed code:
import React from "react";
class SidebarTest extends React.Component {
constructor() {
super();
this.state = {
isDropdownActiveFlowers: false
};
}
displayDropdownFlowers = () => {
this.setState({
isDropdownActiveFlowers: !this.state.isDropdownActiveFlowers
});
console.log(this.state.isDropdownActiveFlowers);
};
render() {
return (
<div className="sidenav">
<h1 className="Sidebar-Name">Flooo</h1>
<button onClick={this.displayDropdownFlowers}>works</button>
<button onClick={this.displayDropdownFlowers}>works</button>
<button onClick={this.displayDropdownFlowers}>works</button>
<button onclick={this.displayDropdownFlowers}>doesnt work</button>
<button onclick={this.displayDropdownFlowers}>doesnt work</button>
</div>
);
}
}
export default SidebarTest;
I think you are using "onclick" instead of "onClick"
I'm working on my first React project. I need to style some parts with JS because I'm dependent on the content. I'm calling the JS function from the components componentDidMount() upon JQuery's $(document).ready() because else it won't find the nodes to style.
When I enter the page or refresh the styling works as planned, but when I use the router's <Link> or <NavLink> the JS won't load.
Is there a way make it work?
class About extends React.Component {
constructor() {
super();
}
componentDidMount() {
$(document).ready( function () {
indentation($('.page__content__career').children(), 85);
indentation($('.page__about .page__content__text').children(), 100);
});
}
render() {
return (
<div className="page page__about">
<div className="page__content page__about__content">
<h1>about</h1>
<hr />
<div className="page__content__photo">
<img src={'../images/profile-picture-square--dark.jpg'} />
</div>
<div className="page__content__text">
<p>Hello, World</p>
</div>
<ul className="page__content__career">
{
cvList.map( (job) => {
return (
<CVElement
startTime={job.startTime}
endTime={job.endTime}
description={job.description}
place={job.place}
link={job.link}
/>
)
})
}
</ul>
</div>
</div>
);
}
}
export default About;
I also tried calling the respected functions when entering, which did not work
<Route path="/en/about" component={About} onEnter={() => console.log('Entered About')}/>
Why use jQuery to style? Why use jQuery at all? Just pass your styles in to the appropriate element using the style={{}} attribute, like this:
<ul className="page__content__career" style={{ marginLeft: '100px' }}>
...
</ul>
Ok, I found it out. It's actually pretty simple. As #Alex Dovzhanyn suggested I got rid of jQuery but then used DOM style objects.
componentDidMount() {
const pageAboutChildren = document.querySelector('.page__about .page__content__text').childNodes;
const pageAboutCareer = document.querySelector('.page__content__career').childNodes;
indentation(pageAboutChildren, 100);
indentation(pageAboutCareer, 85);
changeColor();
}
with indentation() being:
const indentation = (element, indent) => {
for(var i = 0; i < element.length; i++) {
element[i].style.marginLeft = indent * (i+1) + 'px';
}
}
I had some referencing troubles which further confused me
it's not so hard after all
I have been trying since yesterday to make an animation to my image carousel. As far as I understand, you wrap the content to be animated with the CSSTransitionGroup and make sure it stays in the dom and also specify a unique key to each child of the transition group. I believe I have followed all this yet I see no transition.
One thing worth to mention, While I was trying to get this working I suspected if something could be wrong with the key, so I tried setting the key with a random string. The key would change every-time the state changes, and for some unknown reason I could see the animation. Can someone explain this to me.
I am not sure where I am going wrong, whether the version of transition group or in setting the key to children, No clue !
Below is the code replicating my problem.
var CSSTransitionGroup = React.addons.CSSTransitionGroup
class Images extends React.Component {
constructor(props){
super(props);
this.state = {
showComponent: false,
}
}
componentWillReceiveProps(nextProps){
if (this.props.name === nextProps.showComponentName){
this.setState({
showComponent: true,
})
} else {
this.setState({
showComponent: false,
})
}
}
render() {
if (this.state.showComponent){
return (
<img src={this.props.url} />
)
} else {
return null;
}
}
}
class TransitionExample extends React.Component {
constructor (props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state= {
showComponentName: null,
}
}
onClick(button) {
this.setState({
showComponentName: button.currentTarget.textContent,
})
}
render() {
var imageData = [
"http://lorempixel.com/output/technics-q-c-640-480-9.jpg",
"http://lorempixel.com/output/food-q-c-640-480-8.jpg",
"http://lorempixel.com/output/city-q-c-640-480-9.jpg",
"http://lorempixel.com/output/animals-q-c-640-480-3.jpg"
];
var images = [];
for (var i in imageData) {
i = parseInt(i, 10);
images.push(
<Images url={imageData[i]} showComponentName={this.state.showComponentName} name={imageData[i]} key={imageData[i]} />
);
}
return (
<div>
<div>
<button onClick={this.onClick}>{imageData[0]}</button>
<button onClick={this.onClick}>{imageData[1]}</button>
<button onClick={this.onClick}>{imageData[2]}</button>
<button onClick={this.onClick}>{imageData[3]}</button>
</div>
<div className="transitions">
<CSSTransitionGroup
transitionName="viewphoto"
transitionEnterTimeout={2000}
transitionLeaveTimeout={2000}
transitionAppearTimeout={2000}
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}>
{images}
</CSSTransitionGroup>
</div>
</div>
);
}
}
ReactDOM.render(
<TransitionExample />,
document.getElementById('container')
);
I am also providing the link to the example on jsfiddle
The problem with your code is that images is always an array of elements that don't mount/unmount. The correct approach for this is to change the child. For example, if you substitute the return of the render method of your fiddle with this:
return (
<div>
<div>
<button onClick={this.onClick}>{imageData[0]}</button>
<button onClick={this.onClick}>{imageData[1]}</button>
<button onClick={this.onClick}>{imageData[2]}</button>
<button onClick={this.onClick}>{imageData[3]}</button>
</div>
<div className="transitions">
<CSSTransitionGroup
transitionName="viewphoto"
transitionEnterTimeout={2000}
transitionLeaveTimeout={2000}
transitionAppearTimeout={2000}
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}>
<img src={this.state.showComponentName} key={this.state.showComponentName}/>
</CSSTransitionGroup>
</div>
</div>
);
The animation works! Using a simple img instead of your Images component and giving it the image url (this only works when you have clicked a button, showComponentName should be initialized to show the first image). You could also use a custom component of course, but the point here is that the children elements of CSSTransitionGroup must be changed if you want the animation to trigger because otherwise you are always rendering the same four Images components no matter whether they return the img or not. You might want to check out react-css-transition-replace since it usually works better when it comes to replacing.
I try to learn React but seems in the new version there are some changes:
class Reactapp extends React.Component{
constructor(){
super();
}
sayMassage(){
console.log(this.props.children);
}
render(){
var sayMassage = this.sayMassage.bind(this);
return(
<div>
<h3> Hello {this.props.word}</h3>
<p>
<a href="#" onClick={this.sayMassage}>
Click Me
</a>
</p>;
</div>
);
}
}
ReactDOM.render(
<div>
<Reactapp word="React">This is a ReactJS 15.5 Tutorial</Reactapp>
</div>,
document.getElementById('root')
);
This code should work but seems I am missing something.
It should console.log "This is a ReactJS 15.5 Tutorial"
super is called before this so this could not be null.
I tried to bind this but I don't know how, My code seems to fail. The old code with createReactClass had auto-binding tho.
Its a scope issue, just write this line in the constructor, it will work:
this.sayMassage = this.sayMassage.bind(this);
And remove this one:
var sayMassage = this.sayMassage.bind(this);
Check the working example:
class Reactapp extends React.Component{
constructor(){
super();
this.sayMassage = this.sayMassage.bind(this);
}
sayMassage(){
console.log(this.props.children);
}
render(){
return(
<div>
<h3> Hello {this.props.word}</h3>
<p>
<a href="#" onClick={this.sayMassage}>
Click Me
</a>
</p>
</div>
);
}
}
ReactDOM.render(
<Reactapp word="React">This is a ReactJS 15.5 Tutorial</Reactapp>,
document.getElementById('root')
);
<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>
<div id='root'/>
You can do what Mayank said, but there is another way - using arrow functions. That way, the function will automatically take 'this' of the class instance.
It will be something like the below. You will have to remove the binding that you did originally, of course.
class Reactapp extends React.Component{
sayMassage = () => {
console.log(this.props.children);
}
render(){
return(
<div>
<h3> Hello {this.props.word}</h3>
<p><a href="#" onClick={this.sayMassage}>Click Me</a></p>;
</div>
);
}
}
ReactDOM.render(
<div>
<Reactapp word="React">This is a ReactJS 15.5 Tutorial</Reactapp>
</div>,
document.getElementById('root')
);
P.S. There is a typo in the function name, sayMassage (should be sayMessage), unless you actually wanted to say Massage :-)
One option to solve your scoping problem is to move the binding to the class constructor like so:
constructor(props) {
super(props);
this.state = {...};
this.sayThaiMassage = this.sayThaiMassage.bind(this);
}
or using a fat arrow function like so:
myFatGreekMassage = (event ) => {
...your code
}
While fat arrows functions keeping the scope (this is that) your problem is also solved.
First, remove your line var sayMassage = this.sayMassage.bind(this);
It has to be in the constructor, like this :
constructor(){
super();
this.sayMassage = this.sayMassage.bind(this);
}
Or, to keep the context when calling your function and not having to bind, you can use an arrow function in the link, like this :
<a href="#" onClick={() => this.sayMassage}>Click Me</a>
Try onClick={() => ::this.sayMassage}
And remove var sayMassage = this.sayMassage.bind(this);
--
:: here make the bind this with the current scope.
() => the arrow function declare a function which will be executed when you click on the element. If you don't declare the call as a function it will be executed every time you reload the page, without action needed.