Roland
Roland

Reputation: 5234

How to build specialized components in React

I found myself writing code like this, for a bunch of components that have some commonality:

class PropertyBlock extends React.Component {
    render() {
        const { headtext, blockType, selectedId, selectOptions, onChange, extratext } = this.props
        return (
            <div className='propblk'>
                <div className='headline'>{headtext}</div>
                {blockType == 'dropdown' ? <PropBlk_Dropdown selectOptions={selectOptions} selectedId={selectedId} onChange={onChange} />
                    :
                    blockType == 'inp_line' ? <PropBlk_InputLine value={selectedId} onChange={onChange} />
                        :
                        blockType == 'inpstory' ? <PropBlk_InputStory value={selectedId} onChange={onChange} extratext={extratext} />
                            :
                            blockType == 'showline' ? <PropBlk_ShowLine value={selectedId} lines={selectOptions} />
                                :
                                blockType == 'inp_date' ? <PropBlk_InputDate value={selectedId} onChange={onChange} />
                                    :
                                    blockType == 'inp__chk' ? <PropBlk_Checkbox value={selectedId} onChange={onChange} extratext={extratext} />
                                        :
                                        <div>
                                            {selectedId}
                                        </div>

                }
            </div>
        )
    }
}

But somehow I thought that could be better. I was thinking about a parent component and a lot of specialized child components that inherit from that parent. like:

class PropBlk_Type1 extends PropertyParentBlock {
   ...
}

However, reading On React it seems that inheritance should be avoided. So I tried something in line of the React example like this:

class PropertyParentBlock extends React.Component {
    render() {
        const { headtext, myownhtml } = this.props
        return (
            <div className='propblk'>
                <div className='headline'>{headtext}</div>
                {myownhtml}
            </div>
        )
    }
}

class PropBlk_Type1 extends React.Component {
    render() {
        const { headtext, value, onChange, extratext } = this.props
        return (
            <PropertyParentBlock headtext={headtext}
                myownhtml={(
                    <div>
                        <input value={value} onChange={(e) => onChange(e.target.value)} />
                    </div>
                )}
            />
        )
    }
}

So the specialized html is loaded into the parent component as a property myhtml. Building this with WebPack gave no syntax errors, and actually running this creates the desired html and it seems to work.

As I am still learning React I am wondering if I am on the right track, or that there are better ways and best practices for this kind of inheritance or composition.

Upvotes: 0

Views: 505

Answers (2)

Roland
Roland

Reputation: 5234

By reading the React doc more closely, I found out that I was on the right track. Only my example could be simplified by leaving out one pair of parentheses and utilize the special props.children .

I could rewrite the ugly code of the question to something like this 'parent' component:

class PropBlk_Frame extends React.Component {
    render() {
        const { headtext } = this.props
        return (
            <div className='propblk'>
                <div className='headline'>{headtext}</div>
                {this.props.children}
            </div>
        )
    }
}

The above component is used in a number of 'child' components, like:

class PropBlk_InputLine extends React.Component {
    render() {
        const { headtext, value, inputfield, onChange } = this.props
        return (
            <PropBlk_Frame headtext={headtext}>
                <input value={value} onChange={(e) => onChange(true, e.target.value, inputfield)} onBlur={() => onChange(false, null, inputfield)} />
            </PropBlk_Frame>
        )
    }
}

class PropBlk_InputDate extends React.Component {
    render() {
        const { headtext, value, inputfield, onChange } = this.props
        return (
            <PropBlk_Frame headtext={headtext}>
                <input type='date' value={value} onChange={(e) => onChange(true, e.target.value, inputfield)} onBlur={() => onChange(false, null, inputfield)} />
            </PropBlk_Frame>
        )
    }
}

Now I can build my web page with the specialized components like this:

<PropBlk_InputLine headtext='Projectnaam *' value={fields.ProjectNaam} inputfield='inp_projectnaam' onChange={this.onInput} />
<PropBlk_InputDate headtext='Opdrachtdatum *' value={fields.DatumLabOpdracht} inputfield='inp_opdrachtdatum' onChange={this.onInput} />

This approach eliminates the structure with the ternaries, hence saves lines of code, and is more comprehensible.

Upvotes: 0

Giorgi Moniava
Giorgi Moniava

Reputation: 28654

There is no need to nest ternary operators like you have, you can do:

class PropertyBlock extends React.Component {
    render() {
        const { headtext, blockType, selectedId, selectOptions, onChange, extratext } = this.props;
        let element;
        switch(blockType){
           case "dropdown" : 
               element =  <PropBlk_Dropdown selectOptions={selectOptions} selectedId={selectedId} onChange={onChange} />;
                 break;
            // go on here ...
        }
        return (
            <div className='propblk'>
                <div className='headline'>{headtext}</div>
                {element}
            </div>
        )
    }
}

Upvotes: 1

Related Questions