Search code examples
reactjsbabeljs

Uncaught TypeError: Cannot assign to read only property '' of object '#<Object>'


i don't know what difference in this code. class a is component and example is example.js

import React, {Component} from 'react';

const styles = {
    border: {
        display: 'inline-block',
        height: '19px',
        padding: '1px 8px 0',
        border: '2px solid',
        borderRadius: '12px',
        lineHeight: '20px',
        fontSize: '14px',
        verticalAlign: 'top',
    },
    default: {
        display: 'inline-block',
        height: '20px',
        padding: '1px 10px 0',
        borderRadius: '10px',
        lineHeight: '21px',
        fontSize: '13px',
        verticalAlign: 'top',
    },
    state: {
        display: 'inline-block',
        width: '14px',
        height: '13px',
        paddingTop: '1px',
        lineHeight: '14px',
        fontSize: '11px',
        color: '#fff',
        letterSpacing: '-0.5px',
        textAlign: 'center',
        verticalAlign: 'top',
    }
};

class A extends Component {
    static defaultProps = {
        type: 'default',
    };

    render() {
        const {
            label,
            style,
            type,
            ...other
        } = this.props;


        switch (type) {

            case 'border':
                elementStyle = styles.border;
                break;
            case 'default':
                elementStyle = styles.default;
                break;
            case 'state':
                elementStyle = styles.state;
                break;
        }

        return (
            <span style={Object.assign(elementStyle, style)} {...other}>{label}</span>
        );
    }
}

export default A;

and example code is example.js

import A from './A';

    export default class Example extends React.Component {
        render() {
            return (
                <div>
                    <A style={{background: '#fe6969', color: '#fff'}} /> &nbsp;
                    <A style={{background: '#ff8137', color: '#fff'}} /> &nbsp;
                    <A  style={{background: '#fcb400', color: '#fff'}} /> &nbsp;
                </div>
            );
        }
    }

this code error is Uncaught TypeError: Cannot assign to read only property 'background' of object '#'

i use babel-loader 8, babel7 ,webpack4

if i correct Object.assgin({}, elementStyle, style) is working. i think this error occur when rerendering A component. i don't know why this error...

please help me.


Solution

  • All you need to do is concat/merge two objects like this using spread

    {{...elementStyle, ...style}}  or
    
    {Object.assign({}, elementStyle , style) }
    

    You should understand the nature of how Object.assign works. It returns the target object as the return value of its operation.

    So, in the first syntax:

    Object.assign({}, elementStyle , style)
    

    you are creating a brand new object with the enumerable properties of elementStyle and style.

    If you do this:

    Object.assign(elementStyle, style)
    

    Then elementStyle itself is the target object, so that will be mutated and that will be what is returned from Object.assign.

    Here is an example what I mean.

    Example 1 :

    // With no new target object
    const original = [{id:1}, {id:2}, {id:3}];
    
    const newArray = original.map(elem => {
      return Object.assign(elem, {id:2});
    });
    
    console.log('Original object has changed');
    console.log(original);
    
    //------------------------------
    
    // With a new target object
    const original2 = [{id:1}, {id:2}, {id:3}];
    
    const newArray2 = original2.map(elem => {
      return Object.assign({}, elem, {id:2});
    });
    
    console.log('Original object has not changed');
    console.log(original2);

    Example 2 :

    var styles =  {
      circle: {backgroundColor: 'yellow', height: '1005', width: '100%'},
      circleA: {backgroundColor: 'blue'},
    };
    

    So we need all circle to have default cir some circle style, but we need to change some property,

    // background yellow
    <div style={styles.circle}></div>
    
    // background  blue
    <div style={Object.assign(styles.circle, styles.circleA)}></div>
    
    // expeted background yellow, but it's blue. cus styles.circle still have it's merged value
    <div style={styles.circle}></div>
    

    The solution is to pass an empty object to Object.assign(). By doing this, you're telling the method to produce a NEW object with the objects you pass it.

    Example 3:

    const obj1 = {
      name: "J"
    }
    
    const obj2 = {
      gander: "m"
    }
    
    // Here, obj1 is the same after the Object.assign call
    console.log(Object.assign({}, obj1, obj2));
    console.log(obj1)
    console.log(obj2)
    
    console.log("without empty obj passed")
    
    // Note that after this call, obj1 holds both keys. So this will mutate it:
    console.log(Object.assign(obj1, obj2));
    console.log(obj1) // This is different now
    console.log(obj2)

    In your case,

    `<A propstyle={{background: '#fe6969', color: '#fff'}} />
    
    <A propstyle={{background: '#ff8137', color: '#fff'}} /> ` 
    

    component A defined twice in Parent, which means that we will get two circles and child component will render twice.

    and in Child component you defined like below:

    <span style={Object.assign(elementStyle , style) }{...other}>{label}</span>
    

    first render :

    Object.assign overwrite properties from right to left props style to elementStyle,here elementStyle itself is the target object,that will be what is returned from Object.assign.

    style props : { background: "#fe6969", color: "#fff" }

    elementStyle : { background: "#fe6969", borderRadius: "10px", color: "#fff" }

    Second render :

    Object.assign tries to overwrite properties from right to left, but elementStyle have { background: "#fe6969", borderRadius: "10px", color: "#fff" }

    and Object.assign is still in loop (remember example 1 .map())

    style props : { background: "#ff8137", color: "#fff" }

    error thrown: 'TypeError: Cannot assign to read only property 'background' of object ' when {Object.assign(elementStyle , style) } because there's no new target object.

    please find the full code here

    Hope it helps. read more