I am trying to make a component in React where you could iterate +1 or -1 on click. Please look at jsfiddle and tell me where I am missing the point.
Many thanks for all possible help.
Looking forward,
class App extends React.Component {
constructor(props) {
super(props);
this.state = {clickCount: 0};
console.log(this.state)
}
handleClickInc(){
this.setState({count:this.state.clickCount + 1})
}
handleClickDec(){
this.setState({count:this.state.clickCount - 1})
}
render(){
return
<div>
<div>
{this.props.clickCount}
</div>
<button onClick={this.handleClickInc}>{"+"}</button>
<button onClick={this.handleClickDec}>{"-"}</button>
</div>
}
}
ReactDOM.render(
<App/>,
document.getElementById('container')
);`
html part
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
Your problems were:
1) your return function was not wrapped in parens, causing a syntax error.
2) your click handlers were not bound to this (i.e. you needed to call this.handleClickInc.bind(this) or use fat arrow syntax as mentioned above.
3) As mentioned, you were updating the state of count but you meant to update clickCount.
Here is a working fiddle.
https://jsfiddle.net/6z3cuLys/5/
Looks like you could be missing .bind() on there. Without it, this has the wrong context as it fires the function.
try
<button onClick={this.handleClickInc.bind(this)}>{"+"}</button>
<button onClick={this.handleClickDec.bind(this)}>{"-"}</button>
or, fat arrow functions generally do this work for you
<button onClick={() => this.handleClickInc()}>{"+"}</button>
<button onClick={() => this.handleClickDec()}>{"-"}</button>
Well first of all all render elements must have return right? So your return is missing the () wrapping the div.
Second, to use state in a function you have to bind the function. I use to put the statement inside the constructor.
You can put like this:
this.handleClickInc = this.handleClickInc.bind(this);
Make this for the other function and it will work.
Related
I hope you understand this simple example.
I tried to change the background color of my HTML element on first render by handling it in React Component with a bit help of jQuery.
This is the code inside my React Component (the props is passed from state of Parent Component):
class QuoteBox extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
$('#root').css('background-color', this.props.color);
$('#text').css('color', this.props.color);
$('#author').css('color', this.props.color);
}
render() {
return (
<div>
<div id="quote-box" className="quote-box">
<div className="quote">
<span id="text" class="quote-text">{this.props.quote}</span>
</div>
<div id="author" className="quote-author">
<span>- {this.props.author}</span>
</div>
</div>
)
}
The code inside componentDidMount() seem doesn't recognize the this.props.color. But if change to $('#root').css('background-color', 'green'); it's immediately change the background to green on first render.
But in render() it recognize another props.
So what did I do wrong? Is there a way to target HTML element using jQuery inside React?
This most likely happens, because on the first render the props.color is not set. It is most likely, set on the parent element causing a re-render of the QuoteBox, which will not run the componentDidMount which is only run on the first render.
Besides the fact that you should not mix jQuery DOM manipulation with React, just to test your scenario, try using componentDidUpdate instead and it will most likely fix your issue.
I wanted to create a component that exposes a function in the parent.
While the Svelte docs point to context="module", that script gets called only once, which would break functionality if I have several instances of the same component.
I found several examples on the internet and they all point to a very handy workaround, defining the export in the component and calling it with dot notation:
// Child.svelte
<script>
let element
export function changeColor() {
console.log(element)
element.style.backgroundColor = "#" + Math.floor(Math.random() * 16777215).toString(16)
}
</script>
<button bind:this={element}>A button</button>
I created a very simple use case, which works when I bind the component to a variable and call it with child.function, but what if I want to call the function from a click on the component itself?
// App.svelte
<script>
import Child from "./Child.svelte"
let child
</script>
<button on:click={() => child.changeColor()}>Click</button> // This works
<Child bind:this={child} on:click={() => child.changeColor()} /> // This doesn't work
I understand the logic behind the first option, however I don't understand why the second option doesn't work!
Should it be the same? I binded the component to a variable, and I am calling it with the same syntax. How can i make it work, without using a second element that calls the function?
<Child bind:this={child} on:click={() => child.changeColor()} />
doesn't work because on:click is not defined, you can easily see this when changing it to on:click={() => console.log('test')}
The reason it doesn't work is because in Svelte events do not 'bubble' out of components, if you want to do that you have to explicitly indicate this in Child.svelte
<button bind:this={element} on:click>A button</button>
The layout of my app changes depending on some choices made by the user so the same component gets hung under different nodes in the DOM. Unfortunately, React unmounts and re-mounts the component. As a result, my component loses the state is has accumulated. I have tried to add a key property to convey the information that this is the same instance but I got the same results. The below is obviously a SSCCE. Every time the user clicks the button, component A gets unmounted and re-mounted:
class A extends React.Component {
componentWillUnmount = () => {
console.log('map::componentWillUnmount()');
}
componentDidMount = () => {
console.log('map::componentDidMount()');
}
render() {
return (
<div>
this is component A
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state={i:0};
}
increment = () => {
this.setState({i: this.state.i+1});
}
render() {
console.log('app::render');
if (this.state.i % 2 === 0)
return (
<>
<div>
<div>
<A key={42}/>
</div>
</div>
<button onClick={this.increment}>re-render</button>
</>
);
else return (
<>
<div>
<A key={42}/>
</div>
<button onClick={this.increment}>re-render</button>
</>
)
}
}
Just to clarify, my code isn't trying to achieve anything except reproduce the issue, like I said, it's a SSCCE. Surely there are apps where they user can change the layout from a "preferences" menu so that a component ends up in a different place in the DOM depending on the user's preferences. I can't imagine its acceptable to lose the state in such a situation. What is the proper way to deal with this kind of situations?
This is because the conditional rendering is returning two different tree structures where the path to A is different.
React.Fragment > div > div > A
React.Fragment > div > A
Imagine if we're mimicking React by using plain JS to do the mounting and unmounting manually, we will have to:
Store <A/> as a variable
Remove the inner <div/> (<A/> will automatically be removed as well)
Then append the previously stored <A/> into the outer <div/> (which actually is not a good approach either because this assumes <A/> will never need to update itself once it's mounted, even if the props change)
So long as there's a remove and append, it's quite equal to a React component being unmounted and then mounted again.
The code is a bit vague and I cannot tell what it's trying to achieve by having 2 <div/>s in the first condition and only 1 <div/> in the second. But perhaps you can use ternary operators to conditionally render just what you need and leave <A/> alone (and may be a bit of CSS to make the inner <div/> appear as if it is nested inside another <div/>). This way, <A/> will not unmount when <App/> changes state.
return (
<>
<div>
<A />
{condition ? // where `condition` is a boolean
<div style={{
position: 'absolute'
// and may be other styles to achieve the visual trickery
}}>
{/* your content here */}
</div>
:
null
}
</div>
<button onClick={this.increment}>re-render</button>
</>
)
By the way, they say 42 is the answer to everything, but I think not for this case. Setting the key to 42 won't help. 😅
<button on:click={() => (visible = !visible)}>Toggle</button>
{#if !visible}
<QuizArea
transition:slide
on:score={e => {
playerScore = e.detail.score;
}} />
{/if}
My question is can I use the transition without toggling the visibility?
Using a {#key} block:
<script>
import { fly } from "svelte/transition"
let unique = {}
function restart() {
unique = {} // every {} is unique, {} === {} evaluates to false
}
</script>
{#key unique}
<h1 in:fly={{x: 100}}>Hello world!</h1>
{/key}
<button on:click={restart}>Restart</button>
REPL
{#key} was introduced in Svelte v3.28, before that you needed to use a keyed {#each} block with only one item
When the key changes, svelte removes the component and adds a new one, therefor triggering the transition.
Using { create_in_transition } from "svelte/internal"
<script>
import { fly } from "svelte/transition"
import { create_in_transition } from "svelte/internal"
let element;
let intro
function animate() {
if (!intro) {
intro = create_in_transition(element, fly, {x: 100});
}
intro.start();
}
</script>
<h1 bind:this={element}>Hello world!</h1>
<button on:click={animate}>Go</button>
REPL
Has a similar effect, but instead of removing the previous component and creating a new one, this method re-uses the same instance.
But using internal api's is dangerous as these may change when you update svelte.
If you decide to use this, add a line to your project Readme.md mentioning which internal api's you used and why.
Try to write it using other methods when you can.
the transition directive or intro/outro is for transition when your component is created and added into the DOM, or destroyed and removed from the DOM.
The only way to add/remove a component with Svelte is to use logic blocks like {#if} to add/remove a component based on a logic.
If you want to keep the component on the DOM, but still add animation, like fading the component in and out, you can consider using CSS transition or CSS animation by adding/removing CSS class, something like this.
How can I access my key property value inside my dumb component?
I have this dumb component:
const TagSummary = ({ tags, highlightTag }) => {
if (!tags) {
return <div />;
}
return (
<div>
{Object.keys(tags).map((tag) => {
return (
<div key={ tag }>
<button type="button" onClick={ highlightTag }>
<pre><{ tag }></pre>
</button>
<p>{ tags[tag] }</p>
</div>
);
})}
</div>
);
};
The method that I pass into it is this:
highlightTag(event) {
event.preventDefault();
console.log(event.target);
}
I want to be able to retrieve the key property in order to perform some other type of logic. How can I retrieve it onClick?
It isn't the best way to do it, instead you should have button be a seperate component where you can pass the onclick and the key as props and then in the button component merge the two together. The quick and dirty way is as follows
<button type="button" onClick={ highlightTag.bind(this, tag) }>
that will make sure that that argument is always provided to the highlightTag function.
The problem with this though is when React checks to see if anything has changed with the component it will always return true because of the function binding in the render method. If you aren't worried about performance you can leave it that way but that is the pitfall of using the binding in the render method
I'm not sure if you need to get the event back in the highlightTag function, but I would do
<button type="button" onClick={ev => {
ev.preventDefault();
highlightTag(tag);
}}>
This will make your function highlightTag more reusable (call this function programmatically, not from a user interaction for instance). It will also decouple implementation detail of TagSummary with its parent.