干掉 React 代码。改变父组件背景的多个按钮组件

DRY up React code. Multiple button components that change the parent component's background

我确定有一种方法可以 DRY 代码。 changeColor() 所做的只是改变父组件的背景颜色。

import { Play } from "./play";
import { Hello } from "./hello";
import { styles } from "./styles";`

export class Buttons extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            color: styles.container
        };
        this.changeColor = this.changeColor.bind(this);
        this.changeColor1 = this.changeColor1.bind(this);
        this.changeColor2 = this.changeColor2.bind(this);
    }
    changeColor(newColor) {
        this.setState({
            color: styles.backPlay
        });
    }
    changeColor1(newColor) {
        this.setState({
            color: styles.backTime
        });
    }
    changeColor2(newColor) {
        this.setState({
            color: styles.backHello
        });
    }
    render() {
        return (
            <div style={this.state.color}>
                <Play onClick={this.changeColor} />
                <Time onClick={this.changeColor1} />
                <Hello onClick={this.changeColor2} />
            </div>
        );
    }
}

这是样式页面,我也觉得可以稍微DRY一下。 Container, backPlay, backTime 和 backHello 都代表相同的容器,但背景不同。

styles.js

export var styles = {
    loc: {
        padding: 25,
        margin: 40,
        fontWeight: "bold",
        textAlign: "center"
    },
    green: {
        color: "green",
        background: "#59D13E"
    },
    red: {
        color: "yellow",
        background: "#A9A81D"
    },
    blue: {
        color: "blue",
        background: "#34BEE3"
    },
    container: {
        display: "inline-block",
        textAlign: "center",
        marginTop: 50,
        padding: 40

    },
    backPlay: {
        display: "inline-block",
        textAlign: "center",
        background: "yellow",
        marginTop: 50,
        padding: 40
    },
    backTime: {
        display: "inline-block",
        textAlign: "center",
        background: "blue",
        marginTop: 50,
        padding: 40
    },
    backHello: {
        display: "inline-block",
        textAlign: "center",
        background: "green",
        marginTop: 50,
        padding: 40
    },
    mainCont: {
        height: "100vh",
        textAlign: "center",
        background: "#FFA692"
    }
};

更新

我找到了一种更好的方法来 DRY 这段代码。通过使用一个按钮组件并操纵它的状态。让我知道是否有更好的方法来做到这一点。

ButtonContainer.js

import React from 'react'
import Button from './Button'

export default class ButtonContainer extends React.Component {
    state = {
        colors: ['red', 'blue', 'green']
    }
    toggleClass = (color, id) => {
        let colors = [...this.state.colors]
        const newColors = colors.map((newColor, index) => {
            if (id === index) {
                const copyMap = { 0: 'red', 1: 'blue', 2: 'green' }
                const copy = color === 'not' ? copyMap[index] : 'not'
                return copy
            } else {
                return newColor
            }
        })
        this.setState({ colors: newColors })
    }
    render() {
        return (
            <div className='button-container'>
                {this.state.colors.map((color, index) =>
                    <Button
                        toggleClass={this.toggleClass}
                        key={index}
                        id={index}
                        name={color}
                    />
                )}
            </div>
        )
    }
}

Button.js

import React from 'react'

const Button = (props) => (
    <button
        className={`button-component ${props.name}`}
        onClick={() => props.toggleClass(props.name, props.id)}
    >
        {props.name}
    </button>
)

export default Button

_button-container.scss

.button-container {
    margin: 10rem auto;
    text-align: center;
}

_button.scss

.button-component {
    padding: 4rem;
    margin: 0 2rem;
}

.red {
    background: red;
}

.blue {
    background: blue;
}

.green {
    background: green;
}

.not {
    background: none;
}

您可以使用 .bind() 将参数预绑定到函数,然后再将其作为 prop 传递:

export class Buttons extends React.Component {
    state = {
        color: styles.container
    };
    changeColor = newColor => {
        this.setState({
            color: newColor
        });
    };
    render() {
        return (
            <div style={this.state.color}>
                <Play onClick={this.changeColor.bind(this, styles.backPlay)} />
                <Time onClick={this.changeColor.bind(this, styles.backTime)} />
                <Hello onClick={this.changeColor.bind(this, styles.backHello)} />
            </div>
        );
    }
}

您还可以删除构造函数并使用粗箭头函数将您的方法自动绑定到组件。

styles.js

const container = {
    display: "inline-block",
    textAlign: "center",
    marginTop: 50,
    padding: 40
};

export const styles = {
    loc: {
        padding: 25,
        margin: 40,
        fontWeight: "bold",
        textAlign: "center"
    },
    green: {
        color: "green",
        background: "#59D13E"
    },
    red: {
        color: "yellow",
        background: "#A9A81D"
    },
    blue: {
        color: "blue",
        background: "#34BEE3"
    },
    container,
    backPlay: {
        ...container,
        background: "yellow"
    },
    backTime: {
        ...container,
        background: "blue"
    },
    backHello: {
        ...container,
        background: "green"
    },
    mainCont: {
        height: "100vh",
        textAlign: "center",
        background: "#FFA692"
    }
};

可以使用es6 spread operatorstyles.container的内容复制到各个样式中,然后覆盖color属性。

由于您所有的变色函数都非常相似,您可以传入要应用的样式的名称并在函数内部使用它,从而避免重复。

changeColor(attr) {
    this.setState({
        color: styles[attr]
    });
}

render() {
    return (
        <div style={this.state.color}>
            <Play onClick={() => this.changeColor('backPlay')} />
            <Time onClick={() => this.changeColor('backTime')} />
            <Hello onClick={() => this.changeColor('backHello')} />
        </div>
    );
}