将类与JSS合并而不是覆盖

时间:2018-12-07 17:31:30

标签: javascript reactjs jss

我正在使用React和JSS尝试构建一小组可重用的组件,但是在尝试将基础组件组成更复杂的组件时遇到了问题。

当我通过classes属性从父组件向下传递类名时,它会完全覆盖默认样式,而不是合并它们。请参见下面的代码片段进行演示:换行时,文本输入会松开边框样式,仅采用换行器中指定的宽度。

const { Component } = React;
const { render } = ReactDOM;
const injectSheet = reactJss.default;

// TextInput.jsx
const TextInput = (() => {
  const styles = {
    root: {
      border: '1px solid #ccc',
      borderRadius: 3,
    }
  };
  const TextInput = ({ classes, ...rest }) => (
    <input className={classes.root} type="text" {...rest} />
  );
  return injectSheet(styles)(TextInput);
})();

// InputField.jsx
const InputField = (() => {
  const styles = {
    root: {
      display: 'inline-flex',
    },
    label: {
      marginRight: 5,
      width: 40,
    },
    input: {
      width: 80,
    },
  };
  const InputField = ({ classes, id, label }) => (
    <span className={classes.root}>
      <label className={classes.label} htmlFor={id}>{label}</label>
      <TextInput classes={{root: classes.input}} id={id} />
    </span>
  );
  return injectSheet(styles)(InputField);
})();


// Demonstration
class App extends Component {
  render() {
    return (
      <dl>
        <dt>Without Wrapper:</dt>
        <dd><TextInput /></dd>
        <dt>With Wrapper:</dt>
        <dd><InputField id="f" label="Foo"/></dd>
        <dt>What I want:</dt>
        <dd><InputField classes={{input: 'generated-by-TextInput-root generated-by-InputField-input'}} id="b" label="Foo"/></dd>
      </dl>
    );
  }
}

render(<App />, document.getElementById('root'));
.generated-by-TextInput-root {
  border: 1px solid #ccc;
  border-radius: 3px;
}

.generated-by-InputField-input {
  width: 80px;
}
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jss/9.8.7/jss.min.js"></script>
<script src="https://unpkg.com/react-jss@8.6.1/dist/react-jss.min.js"></script>
<div id="root"></div>

我想应用该类,而不是简单地覆盖内部组件生成的类名,然后再应用外部组件的自定义样式。本质上,我想以一种 additive 的方式来使classes道具起作用。

有什么办法优雅地做到这一点吗?

5 个答案:

答案 0 :(得分:1)

我们追求压倒性的行为。在某些情况下,您想在其他一些类别中添加您的类,但您想覆盖它们,但我认为我们不能同时拥有两者。

答案 1 :(得分:1)

我认为您需要遵循以下内容:

...
const TextInput = ({ classes, classesFromParent, ...rest }) => (
    <input className={classes.root + ' ' + classesFromParent.root} type="text" {...rest} />
);
...
const InputField = ({ classes, id, label }) => (
    <span className={classes.root}>
      <label className={classes.label} htmlFor={id}>{label}</label>
      <TextInput classesFromParent={{root: classes.input}} id={id} />
    </span>
);

答案 2 :(得分:1)

这是解决问题的另一种方法。

与其尝试从父级传递一个要与子级合并的类,不如使用父级的样式(例如'& > input': { width: 80 })来影响子级。

const { Component } = React;
const { render } = ReactDOM;
const injectSheet = reactJss.default;

// TextInput.jsx
const TextInput = (() => {
  const styles = {
    root: {
      border: '1px solid #ccc',
      borderRadius: 3,
    }
  };
  const TextInput = ({ classes, ...rest }) => (
    <input className={classes.root} type="text" {...rest} />
  );
  return injectSheet(styles)(TextInput);
})();

// InputField.jsx
const InputField = (() => {
  const styles = {
    root: {
      display: 'inline-flex',
      '& > input': {
        width: 80
      }
    },
    label: {
      marginRight: 5,
      width: 40,
    }
  };
  const InputField = ({ classes, id, label }) => (
    <span className={classes.root}>
      <label className={classes.label} htmlFor={id}>{label}</label>
      <TextInput id={id} />
    </span>
  );
  return injectSheet(styles)(InputField);
})();


// Demonstration
class App extends Component {
  render() {
    return (
      <dl>
        <dt>Without Wrapper:</dt>
        <dd><TextInput /></dd>
        <dt>With Wrapper:</dt>
        <dd><InputField id="f" label="Foo"/></dd>
      </dl>
    );
  }
}

render(<App />, document.getElementById('root'));
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jss/9.8.7/jss.min.js"></script>
<script src="https://unpkg.com/react-jss@8.6.1/dist/react-jss.min.js"></script>
<div id="root"></div>

答案 3 :(得分:1)

我创建了一个问题来调查是否可以/应该将合并行为添加到react-jss https://github.com/cssinjs/jss/issues/933

答案 4 :(得分:0)

与其他解决方案之一类似,但与其他类一起使用时更健壮。我们可以使用Object.values(classes).join(' ')来加入传递给TextInput组件的所有类。

请注意,我还必须给全班同学一个不同的钥匙。 classes={{root: classes.input}}成为classes={{input: classes.input}}

const { Component } = React;
const { render } = ReactDOM;
const injectSheet = reactJss.default;

// TextInput.jsx
const TextInput = (() => {
  const styles = {
    root: {
      border: '1px solid #ccc',
      borderRadius: 3,
    }
  };
  const TextInput = ({ classes, ...rest }) => (
      <input className={Object.values(classes).join(' ')} type="text" {...rest} />
  );
  return injectSheet(styles)(TextInput);
})();

// InputField.jsx
const InputField = (() => {
  const styles = {
    root: {
      display: 'inline-flex',
    },
    label: {
      marginRight: 5,
      width: 40,
    },
    input: {
      width: 80,
    },
  };
  const InputField = ({ classes, id, label }) => (<span className={classes.root}>
      <label className={classes.label} htmlFor={id}>{label}</label>
      <TextInput classes={{input: classes.input}} id={id} />
    </span>
  );
  return injectSheet(styles)(InputField);
})();


// Demonstration
class App extends Component {
  render() {
    return (
      <dl>
        <dt>Without Wrapper:</dt>
        <dd><TextInput /></dd>
        <dt>With Wrapper:</dt>
        <dd><InputField id="f" label="Foo"/></dd>
        <dt>What I want:</dt>
        <dd><InputField classes={{input: 'generated-by-TextInput-root generated-by-InputField-input'}} id="b" label="Foo"/></dd>
      </dl>
    );
  }
}

render(<App />, document.getElementById('root'));
.generated-by-TextInput-root {
  border: 1px solid #ccc;
  border-radius: 3px;
}

.generated-by-InputField-input {
  width: 80px;
}
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jss/9.8.7/jss.min.js"></script>
<script src="https://unpkg.com/react-jss@8.6.1/dist/react-jss.min.js"></script>
<div id="root"></div>