I have 2 JS classes: App.jsx, Note.jsx.
Note.jsx:
export default class Note{
constructor(text) {
this.text = text;
}
changeNoteStatus(){
console.log(this.text);
}
}
In App.jsx:
let tmpNote = new Note(String(this.state.value));
this.notes.push({key:this.currId, value:tmpNote});
and in App.jsx i can call the changeNoteStatus() on a simple object but after creating a list of Note and using map() function:
return this.notes.map((obj) =>
<div className="note" onClick={obj.value.changeNoteStatus} key={obj.key} id={"note_" + obj.key}/>
)
The method doesn't recognize class field anymore (like this.text).
I have tried {this.notes[obj.key].changeNoteStatu} in mapping function too.
Please help me.
When you use it in an onClick like that, the handler will get called with the wrong this value.
One solution is to use Function#bind:
onClick={obj.value.changeNoteStatus.bind(obj.value)}
// --------------------------------^^^^^^^^^^^^^^^^
...but depending on how your overall code is structured, there may be more concise or efficient ways of doing it.
Related
I'm trying to learn vanilla webcomponents and I am stuck trying to do something simple and no combination of keywords I can think of return anything helpful in Google.
So in my render method, I see all sorts of examples that manually construct a tag and assign all the attribute, like so:
render()
{
this.innerHTML = `
${
this.data
.map(reason =>
`<${ReasonItem.elementName}
name="${reason.Name}"
description="${reason.Description}">
</${ReasonItem.elementName}>`)
.join('')
}
`;
}
This works, but it's extremely tedious for child controls that have LOTS of attributes.
I would like to do something like:
render()
{
this.innerHTML = `
${
this.data
.map(reason =>
{
let child = new ReasonItem();
child.reason = reason;
child.render();
return child.outerHTML;
})
.join('')
}
`;
This almost works, but apparently the constructor and other methods on an HTMLElement can be called out of order so I'm getting 'unidentified' in all my elements since the constructor calls render() and the setting of the property calls render(), but the constructors render is being called after the property is set, so I'm thinking I'm not doing this right.
Thank you for your time and attention.
since the constructor calls render()
That should never be done in the constructor. Leave that call to connectedCallback.
but the constructor's render is being called after the property is set
That's not possible unless you have an designed an async constructor.
Also, if you're not using Stencil or lit which do property binding and updating for you, you shouldn't try to mimic a render method to react to updates.
Instead, create your elements programmatically and have the component store references to those elements, so you can make targeted, partial updates later. Use getters and setters for the purpose.
Edit: Your solution code can be simplified:
render()
{
while(this.lastChild) this.lastChild.remove();
this.append(...this.data.map(reason => Object.assign(new ReasonItem, { reason })))
}
Also you probably need to empty your host element before rendering if you insist on a render() method.
#connexo So after lots of guess and testing, this appears to do what I need:
render()
{
this.data.forEach((reason) =>
{
const el = document.createElement(ReasonItem.elementName);
el.reason = reason;
this.appendChild(el);
});
}
I like the look of this better, not sure if it's equivalent or advisable, perhaps javascript pros can tell me what's 'generally preferred'?
render()
{
this.data.forEach((reason) =>
{
const el = new ReasonItem();
el.reason = reason;
this.appendChild(el);
});
}
Internally in my 'ReasonItem', the 'reason' property is observed and when changed, it loads (from the complex reason json object) all the dozens of attributes into their appropriate spots on the ReasonItem component.
Suppose we have a method inside a class like this
class Blog extends Component {
postClicked = (id) => {
this.setState({selectedPostId: id})
}
render () {
const newPosts = this.state.posts.map(el => {
return <Post key={el.id}
title={el.title}
author={el.author}
onClick={this.postClicked(el.id)}/>
})
return
//something
{post}
}
}
}
Now, What is the difference between calling the handler like this
onClick={this.postClicked(el.id)} and onClick={() => this.postClicked(el.id)}
Would appreciate if someone can tell me the difference in general
after Ecmascript 6 javascript was introduced with is arrow function link
here ()==>{//code} is a similar as a function() or anonymous function
tell me if you find out what you want
The first option, "this.postClicked(el.id)", will actually call the method, "this.postClicked", with the "el.id" argument, each time the component renders (probably not what's intended).
The second option, "() => this.postClicked(el.id)", will only call the method, "this.postClicked", with the "el.id" argument, when "Post" is clicked.
Overall, if you can find a way to put the "el.id" argument into an "id" or "name" prop on the component
<Post id={el.id} />
then you can do:
<Post
id={el.id}
onClick={this.postClicked}
/>
this.postClicked = (event) => {
const { id } = event.target;
...
}
This last option avoids the use of an unnamed function. If you use an unnamed function, it will cause unnecessary re-renders. React cannot tell that an unnamed function is the same when it's checking whether or not it should re-render, by considering if the props of a component have changed. It considers the unnamed functions to be a new prop each time it checks, causing an unnecessary re-render each time.
Overall, it won't break your app, but it slows down performance slightly if you do it enough. It comes up especially if you start using React Motion (you'll really notice a difference there). It's best to avoid unnamed functions if possible.
you can read this blog it wil clear the things https://medium.com/#machnicki/handle-events-in-react-with-arrow-functions-ede88184bbb
Differences are,
First method is a wrong implementation and it wont give the intended result, where as second one will work.
In the first method you are making a function call, in second one you are assigning a function's signature to onClick.
It is like the combination of below two statements.
var variableName = function(){//some content};
onClick={variableName}
It looks like you question has already been answered. Just a side note though: remember that when assigning your method with an arrow function
onClick={ () => this.method() }
a new anonymous function is created on every re-render. So if the method doesn't need any arguments, it's better to reference the method directly (without parentheses so it's not invoked).
onClick={ this.method }
The first will call the function every time render is done.
The second will do what you want - call it onClick.
Can I write React lifecycle methods as class properties?
I've been using class properties for a while as I like the fact that I no longer have to manually bind my methods, but I'd like to keep some consistency across my components and I'm wondering if there is any drawback on writing the React lifecycle methods as class properties
import React, { Component } from 'react';
class MyComponent extends Component {
render = () => {
return (
<div>Foo Bar</div>
);
}
}
export default MyComponent;
For example, is the context of this class property affected compared to the context in an equivalent method. Given that the render method in the above code is written as an arrow function, this concern seems relevant.
In a way, the true answer depends on your build pipeline and what the resulting Javascript output looks like. There are two primary possibilities:
Input Code
Let's start by saying you are writing the following before going through any sort of pipeline transformations (babel, typescript, etc):
class Test {
test = () => { console.log('test'); };
}
Output as class member variable.
In one possible world, your pipeline will also be outputting the test function as a member variable for the output class. In this case the output might look something like:
function Test() {
this.test = function() { console.log('test'); };
}
This means that whenever you write new Test() the test function is going to be recreated every single time.
Output as class prototype function
In the other major possibility, your pipeline could be recognizing this as a function property and escape it from the class instance to the prototype. In this case the output might look something like:
function Test() {
}
Test.prototype = {
test: function() { console.log('test'); }
}
This means that no matter how many times you call new Test() there will still be only one creation of the test function around in memory.
Desired behavior
Hopefully it's clear that you want your end result to have the function end up on the prototype object rather than being recreated on each class instance.
However, while you would want the function to not end up as a property, that doesn't necessarily mean you couldn't write it that way in your own code. As long as your build chain is making the correct transformations, you can write it any way you prefer.
Although, looking at the default babel settings (which your babeljs tag leads me to believe you are using) it does not make this transformation for you. You can see this in action here. On the left I've created one class with the function as a property and one class with the function as a class method. On the right hand side, where babel shows it's output, you can see that the class with the function as a property still has it being an instance-level property, meaning it will be recreated each time that class's constructor is called.
I did find this babel plugin, which seems like it might add this transformation in, but I've not used it for myself so I'm not positive.
In my experience, the most reason for writing a method as a class property is when the method will be passed as a callback, and you need it to always be bound to the instance. React lifecycle methods will always be called as a method, so there's no reason to bind them (and you incur a tiny memory penalty when you do). Where this makes a difference is when you're passing a function to a component as a callback (e.g. onClick or onChange).
Take this example:
class BrokenFoo extends React.Component {
handleClick() {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
The function represented by this.handleClick is not automatically bound to the component instance, so when the method tries to read the value of this.props it will throw a TypeError because this is not defined. Read this article if you're not familiar with this; the problem described in section 4.2 "Pitfall: extracting methods improperly" is essentially what's happening when you pass around a method without making sure it's bound correctly.
Here's the class, rewritten with the handler as a class property:
class HappyFoo extends React.Component {
handleClick = () => {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
Effectively, you can think of the handleClick definition in the second example as placing this code into the component's constructor (which is just about exactly the way Babel does it):
this.handleClick = () => {
alert(this.props.message);
}
This achieves the same thing as calling bind on the function (as described in the linked article) but does it a little differently. Because this function is defined in the constructor, the value of this in this.props.message is bound to the containing instance. What this means is that the function is now independent of the calling context; you can pass it around and it won't break.
The rule of thumb that I follow: by default, write methods as methods. This attaches the method to the prototype and will usually behave the way you'd expect. However, if the method is ever written without parentheses (i.e. you're passing the value and not calling it), then you likely want to make it a class property.
I am fairly new to react, I was looking at code and I ran into an event
code inside of a class that looked like
zipValueChanged(event) {
const zip = event.target.value;
this.setState({
zipValue: zip,
})
I was wondering why the event function does not have the function keyword anywhere, I did see a binding in the constructor ( this.zipValueChanged = this.zipValueChanged.bind(this);) However, I do not understand why this works.
Thank you so much!
This (this.zipValueChanged = this.zipValueChanged.bind(this);) binding in constructor was done so that any method defined in the class can work.
You need to bind your method in your constructor in order to work it properly -
this.yourFunction = this.yourFunction.bind(this);
This is the new syntax for Class methods definition. You can check out documentation here.
From now on inside a class you can define methods this way :
class A{
render() {
//your code here
}
}
I want to pass a function defined in my actions to an element.
Reducer
case 'UPDATE_HEADER':
return Object.assign({}, state, {
headerChildren: state.headerChildren.concat([action.child])
});
Action.js
export const deleteHeader = () => {
return {
type: 'DELETE_HEADER',
};
}
I am passing this action to my sidebar
onAddHeader () {
this.props.addHeader();
this.props.updateHeader(
`<Header key='${this.props.childKey}'
deleteHeader='${this.props.deleteHeader}'/>`,
);
}
The deleteHeader function is being passed to the Header component but the problem is that it is being passed as a string and not as a function.
I am rendering it as this
`{this.props.headerChildren.map((value, index) => (
<JsxParser
key={index}
components={[Header]}
jsx={value}
/>
))}`
Can someone please guide me through the solution. I have searched a lot and couldn't find a way to do it. If this is not the correct approach to do so, kindly let me know the proper way to do it. I am new to React + Redux so finding it a bit difficult.
Thanks in advance
I am going to take a stab at guessing what I think you want to achieve here - it is a bit unclear so if I am wrong please feel free to comment and I can try and fix.
You don't need to use a string template if you are not trying to pass a string. You can simply pass the component and props themselves. Something like this...
// I'm assuming you're doing something like this
import Header from './wheverYourHeaderIs';
...
onAddHeader () {
this.props.addHeader();
this.props.updateHeader(Header,
{ key: this.props.childKey,
deleteHeader: this.props.deleteHeader
}
);
}
I turned your props into 1 keyed object so it is flexible to add more/less props without having to modify the number of arguments your updateHeader function has to take.
Then your updateHeader would do something like :
function updateHeader(Component, props) {
// do whatever you were going to do with your component
<Component {...props} />
// becasue we combined props into one object
// this is the same as <Header key={this.props.childKey} deleteHeader={this.props.deleteHeader}/>
}
Not sure if this is what you were trying to achieve please feel free to comment if not.
You don't need to use $ or single quotes when passing props these kind of props.. You just need to enclose the value to be evaluated in {}.. It is only when you need to pass a string literal do you use single quotes.
So use something like this instead
<Header key={this.props.childKey} deleteHeader={this.props.deleteHeader}/>