React error on changing the inline-style of a component - javascript

I want to change the style of a component's element on some event, like a click. But when i do, i get the error that my style object is read-only when its not defined as such and thus immutable. Even defining the style object outside the class creates the same error. I created a simple example to illustrate my problem below.
Error:
Uncaught TypeError: Cannot assign to read only property 'color' of object '#<Object>'
profile.tsx
import * as React from 'react';
export default class Profile extends React.Component {
public styler = {
color:"white",
}
render() {
return (<button style={this.styler} onClick={this.HandleMenuClick.bind(this)}>Click me!</button>);
}
HandleMenuClick() {
this.styler.color = 'gray'; // error on executing this line
}
}
Error created upon clicking the button.
Same error is created when moving the style outside of the class:
var styler = {
color:"white",
}

You should use the this key word when inside a class. But to see changes in react you should use a state.
Here is a small example:
class Profile extends React.Component {
constructor(props) {
super(props);
this.state = {
styler:{
color: "white"
}
}
}
render() {
const {styler} = this.state;
return (
<button style={styler} onClick={this.HandleMenuClick}>
Click me!
</button>
);
}
HandleMenuClick = () => {
const { styler } = this.state;
this.setState({styler: {
...styler,
color: 'gray',
backgroundColor: '#000'
}})
};
}
ReactDOM.render(<Profile />, 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"></div>
Edit
As a followup to your comment:
But out of curiosity, my this.styler.color variable never changes in
code, i know it wont call render but it doesnt even change the
variable in code and just gives the error, why is that?
As i mentioned above, you should use the this key word to attach objects to the class which basically just a sugar around the prototype pattern
JavaScript classes are introduced in ECMAScript 6 and are syntactical
sugar over JavaScript's existing prototype-based inheritance. The
class syntax is not introducing a new object-oriented inheritance
model to JavaScript. JavaScript classes provide a much simpler and
clearer syntax to create objects and deal with inheritance.
In your code you used the public key word which i'm not familiar with in this context (is it a typo?) + you should initialize objects inside the constructor.
here is an example with the object being mutated:
class Profile extends React.Component {
constructor(props) {
super(props);
this.myObject = {
myKey: "Hi there!"
};
}
render() {
return <button onClick={this.HandleMenuClick}>Click me!</button>;
}
HandleMenuClick = () => {
console.log(this.myObject);
this.myObject = {
myKey: "Bye there!"
};
console.log(this.myObject);
};
}
ReactDOM.render(<Profile />, 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"></div>

Change your code as -
import * as React from 'react';
export default class Profile extends React.Component {
constructor()
{
super();
this.state.styler = {
color:"white"
}
}
render() {
return (<button style={this.styler} onClick={this.HandleMenuClick.bind(this)}>Click me!</button>);
}
HandleMenuClick() {
this.setState({'styler' : {'color':'gray'}});
}
}

Related

Cross Cutting Concept in react life cycle methods

I want add some behavior on a given lifecycle method of a React application without having to define it in every one of them?
I came from Java world and have been trying to use HOC for printing/console at every react component life cycle methods similar to AOP concept in Spring/Javaor you can say universal cross cutting on life cycle methods like componentWillUnmount, componentDidMount, componentWillMount... I want to console component name and lifecyle methods name.
Example
Component B componentWillMount called
Component A componentWillMount called
Component B componentDidMountcalled ...
I have tried to use HOC correct me if am wrong but it seems I will be forced to pass all components through this function.
Extend React lifecycle hook (e.g add a print statement on every ComponentDidMount) similar question was asked before but the solution only prints the parent components life cycle but not the child?
Thank you for your help and I really appreciate if you include a code snippet.
The only solution in React that I can think of would be to create a new base class that extends the base React Component class. Add the lifecycle method into the base class and then every component you create extends this new class if you want it to use the lifecycle method.
class NewBaseClass extends React.Component {
componentDidMount() {
console.log('do something on mount')
}
}
class CustomComponent extends NewBaseClass {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
I havent tested this exact use case let me know if it helps :)
You can implement an HOC similar to the following:
function withLifeCycleLogs(WrappedComponent) {
const Enhanced = class extends React.Component {
componentDidMount() {
console.log(`Component ${WrappedComponent.name} did mount`);
}
componentDidUpdate(prevProps) {
console.log(`Component ${WrappedComponent.name} did update`, {
// Uncomment below lines to inspect props change
// prevProps,
// nextProps: this.props
});
}
componentWillUnmount() {
console.log(`Component ${WrappedComponent.name} will unmount`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
// Wrap the display name for easy debugging
// https://reactjs.org/docs/higher-order-components.html#convention-wrap-the-display-name-for-easy-debugging
Enhanced.displayName = `WithLifeCylceLogs${getDisplayName(WrappedComponent)}`;
// Static Methods Must Be Copied Over
// https://reactjs.org/docs/higher-order-components.html#static-methods-must-be-copied-over
//
// hoistNonReactStatic(Enhanced, WrappedComponent);
return Enhanced;
}
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || "Component";
}
class Counter extends React.Component {
render() {
return (
<div>
<button onClick={this.props.increment}>Increment</button>
<p>{this.props.counter}</p>
<button onClick={this.props.unmount}>Unmount Counter</button>
</div>
);
}
}
const CounterContainer = withLifeCycleLogs(Counter);
class App extends React.Component {
constructor() {
super();
this.state = {
counter: 0,
counterVisible: true
};
}
increment = () => {
this.setState((state) => ({ ...state, counter: state.counter + 1 }));
};
unmountCounter = () => {
this.setState((state) => ({ ...state, counterVisible: false }));
};
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{this.state.counterVisible && (
<CounterContainer
counter={this.state.counter}
increment={this.increment}
unmount={this.unmountCounter}
/>
)}
</div>
);
}
}
const AppContainer = withLifeCycleLogs(App);
const rootElement = document.getElementById("root");
ReactDOM.render(
<AppContainer />,
rootElement
);
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<div id="root">
</div>
Yes, you need to pass every component to withLifeCycleLogs function. But this is really simple and does not use much space, check this out:
class AwesomeComponent extends React.Component {
//
}
export default withLifeCylceLogs(AwesomeComponent);
It's like using annotation in Spring (correct me if I'm wrong)
CodeSandbox

How to declare a variable for inner function in react component?

when i try to use variable in my class function getting this error
Cannot read property 'zoomInIndex' of undefined
it is working fine if i have one function, can some one help me out how to use the variable in inner function or how to bind the inner function context?
class MyContainer extends Component {
constructor(props) {
super(props);
this.testClick = this.testClick.bind(this);
this.zoomClick = this.zoomClick.bind(this);
this.testVarible= "this is a test";
}
zoomClick(inout) {
return function(e) {
console.log(this.testVarible); // this is not working
}
}
testClick(){
console.log(this.testVarible);
}
}
if i use fat arrow functions it is working fine
zoomClick = inout => e => {
console.log(this.testVarible);
}
but i don't want to use fat arrow functions in react components since i ran into a lot of issues with my webpack configuration.
so my question is how to use the variable in inner function or how to bind the inner function context with out fat arrow syntax?
You need to bind the function returning from the zoomClick():
zoomClick(inout) {
return function(e) {
console.log(this.testVarible); // this is not working
}.bind(this); // <-- bind(this)
}
The anonymous function which was returned had no context of this and thus it was giving the error. Because zoomClick is already bound in the constructor, you just need to bind the function(e) { ... }
You can bind it to the class context one of two ways:
returnFunction = returnFunction.bind(this); or function() {}.bind(this).
class MyContainer extends React.Component {
constructor(props) {
super(props);
this.testClick = this.testClick.bind(this);
this.zoomClick = this.zoomClick.bind(this);
this.testVarible = "this is a test";
}
zoomClick(inout) {
let returnFunction = function(e) {
console.log(this.testVarible);
return this.testVarible;
};
returnFunction = returnFunction.bind(this);
return returnFunction;
}
testClick(){
console.log(this.testVarible);
}
render() {
return (
<div>{this.zoomClick(false)(false)}</div>
);
}
}
ReactDOM.render(
<MyContainer/>,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
i don't want to use fat arrow functions in react components since i ran into a lot of issues with my webpack configuration
I'm guessing you tried to use class fields with the Babel plugin and that caused you some grief.
It's OK to use the traditional method of binding in the constructor and use arrow functions elsewhere in the code without running into those problems.
class MyContainer extends React.Component {
constructor(props) {
super(props);
this.zoomClick = this.zoomClick.bind(this);
this.testVarible = "this is a test";
}
zoomClick() {
return (e) => {
console.log(this.testVarible);
}
}
render() {
return <Button handleClick={this.zoomClick} />
}
}
function Button({ handleClick }) {
return <button onClick={handleClick()}>Click</button>
}
ReactDOM.render(
<MyContainer /> ,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="container"></div>

Convert ng-click code to react component

I have this following button with ng-click
<button ng-click="productClass.saveProductMehod()">Save</button>
and i try to convert it to React
<button-component on-submit="productClass.saveProductMehod"
value="'Save'"></button-component>
with
class ButtonComponent extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.onSubmit();
}
render() {
return (
<button onClick={this.handleClick}>
{this.props.value}
</button>
);
}
}
ButtonComponent.propTypes = {
value: PropTypes.string.isRequired,
onSubmit: PropTypes.func.isRequired,
};
export { ButtonComponent }
ProductClass
class ProductClass {
saveProductMehod() {
this.submitted = true;
//save the product
}
}
The problem here is when the i click the button i loose this initialization from constructor of the ProductClass and when it gets in the saveProductMethod the this is the props object. What am i missing?
There are a few pieces missing here:
<button-component on-submit="productClass.saveProductMehod"
value="'Save'"></button-component>
Should be (noting the quotes around save and the quotes onSubmit, as well as the case):
<ButtonComponent onSubmit={productClass.saveProductMehod}
value="Save"></ButtonComponent>
Let us assume your angular ng-click to be:
<button ng-click="onClick()">Save</button>
Analogous React Component (Stateless Form) would be:
const ReactButton = props =>(<button onClick={props.onClick} value={props.value}/>)
Analogous React Component (Class Based Form) would be:
class ReactButton extends React.Component{
render(){
return(<button
onClick={this.props.onClick}
value={this.props.value}/>)
}
}
Usage of this ReactButton:
Thats it!
Important Points to keep in mind:
Custom Component follow convention that they start with Capital
Alphabet and follow camel case syntax.
JSX is just like html. Only class -> className.
Every Component Name should be a valid JS variable
The new method must be binded to the controller in order to have access to predefined attributes:
this.saveProductMehod = this.saveProductMehod.bind(this);

How to avoid using inline functions in React (ES6) for functions that take arguments

I'm trying to follow the suggestion in this react-eslint doc to avoid using inline functions.
I have a div with an onClick funciton like so:
onClick={ () => this.props.handleClick(some_constant) }
This works perfectly fine, however I don't want to have an inline function. When I try to abstract it by following the pattern in the provided link above, I get a setState error that runs infinitely.
class Foo extends React.Component {
constructor() {
super();
this._handleClickWrapper = this.handleClickWrapper.bind(this);
}
_handleClickWrapper() {
// handleClick is a callback passed down from the parent component
this.props.handleClick(some_constant)
}
render() {
return (
<div onClick={this._handleClickWrapper}>
Hello!
</div>
);
}
}
What needs to be done so that I can avoid using inline functions?
Edit:
I made a serious typo, but in my code, I have what is currently reflected and it is still causing the error.
You bound the wrong function to this. It should be:
this._handleClickWrapper = this._handleClickWrapper.bind(this);
This way _handleClickWrapper will always be bound to the context of the component.
If you really really really want to follow the jsx-no-bind rule, you can create a new component and pass someConstant in as a prop. Then the component can call your callback with the value of someConstant:
class FooDiv extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
render() {
return <div onClick={this.handleClick}>Hello!</div>
}
handleClick() {
this.props.onClick(this.props.someConstant);
}
}
Then your Foo component can just do this:
class Foo extends React.Component {
render() {
const someConstant = ...;
return (
<FooDiv
onClick={this.props.handleClick}
someConstant={someConstant}
/>
);
}
}
Having said that, I would recommend not following jsx-no-bind and just use bind or arrow functions in render. If you're worried about performance due to re-renderings caused by using inline functions, check out the reflective-bind library.
There is a typo
this._handleClickWrapper = this.handleClickWrapper.bind(this);
should be
this._handleClickWrapper = this._handleClickWrapper.bind(this);
in your constructor you forgot to pass props to super()
constructor(props) {
super(props);
this._handleClickWrapper = this._handleClickWrapper.bind(this);
}
Tipp: You can avoid binding (and even the constructor) by using arrow functions declaration inside the class (babel-preset-es2016).
class Foo extends React.Component {
state = {} // if you need it..
onClick = () => {
this.props.handleClick(some_constant)
}
render() {
return (
<div onClick={this.onClick}>
Hello!
</div>
);
}
}
This way you components gets smaller, and easier to read.
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding

Why console.log(super) in React Component constructor throw an Error?

I want to console the super in my InheritComponent constructor method.
But in chrome console, it throw an Error. Why?
class BaseComponent extends React.Component{
static defaultProps = {
title: 'Learn React By Examples'
}
constructor(props) {
console.log(props);
super(props);
}
setTitle(title) {
document.title = title || this.props.title;
return document.title;
}
}
class InheritComponent extends BaseComponent{
state = {
title: ''
};
constructor(props) {
super(props);
//here throw an Error. Why? I want to console.log `super`
console.log(super);
}
componentDidMount() {
const title = this.setTitle('组件继承')
this.setState({title});
}
render() {
return <div>
<p>I inherit BaseComponent</p>
<p>current title is {this.state.title}</p>
</div>
}
}
ReactDOM.render(
<InheritComponent />,
document.body
)
<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>
Above is my demo code.
Reason is simple: super is a keyword, not a function nor any variable. You cannot log super the same as you cannot log var or new keywords.
If you want to log the constructor of your parent, you can try:
console.log(super.constructor);
In fact, super() is just shorthand for super.constructor().
See more: https://developer.mozilla.org/pl/docs/Web/JavaScript/Reference/Operators/super
super is a keyword, you can't use it like a variable. The only allowed ways to use super are outlined in the MDN documentation for it:
super([arguments]); // calls the parent constructor.
super.functionOnParent([arguments]);
If you want to print the parent class, use
console.log(super.constructor) instead.

Categories