简体   繁体   English

将类与JSS合并而不是覆盖

[英]Merge classes with JSS instead of overriding

I'm using React and JSS to try to build a small set of reusable components, but I'm running into issues trying to compose base components into more complex ones. 我正在使用React和JSS尝试构建一小组可重用的组件,但是在尝试将基础组件组成更复杂的组件时遇到了问题。

When I pass class names down from a parent component via the classes prop, it completely overrides the default styles instead of merging them. 当我从父组件传递类名称下通过classes道具,它完全覆盖合并它们的默认样式来代替。 See the snippet below for a demonstration: when wrapped, the text input looses the border styles and only takes on the width specified in the wrapper. 请参见下面的代码片段进行演示:包装后,文本输入将松开边框样式,仅采用包装中指定的宽度。

 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> 

Instead of simply overriding the class name generated by the inner component, I want to apply that class, then the outer components custom styles on top of it. 我不仅要覆盖内部组件生成的类名,还想应用该类,然后应用外部组件的自定义样式。 Essentially, I would like to make the classes prop work in an ' additive ' way. 本质上,我想使classes道具以“ 加法 ”方式工作。

Is there any way to do this gracefully? 有什么办法可以优雅地做到这一点吗?

We went for overriding behaviour. 我们追求压倒性的行为。 In some cases you want to add your class in some others you want to override them, but I don't think we can't have both at the same time. 在某些情况下,您想在其他一些类中添加您的类,但您想覆盖它们,但是我认为我们不能同时拥有这两个类。

I think you want something along the lines of the following: 我认为您需要以下方面的帮助:

...
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>
);

Here is an alternative way to approach the problem. 这是解决问题的另一种方法。 Instead of trying to pass a class from the parent to be merged in with the child's classes, influence the child using the parent's styles (eg '& > input': { width: 80 } ). 与其尝试从父级传递要与子级的类合并的类,不如使用父级的样式(例如'& > 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> 

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

Similar to one of the other solutions, but more robust to work with additional classes. 与其他解决方案之一类似,但是与其他类一起使用时更健壮。 We can use Object.values(classes).join(' ') to join all the classes passed to to the TextInput component. 我们可以使用Object.values(classes).join(' ')连接所有传递给TextInput组件的类。

Note that I also had to give the class a different key. 请注意,我还必须给班级一个不同的键。 classes={{root: classes.input}} became classes={{input: classes.input}} . 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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM