我正在使用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
道具起作用。
有什么办法优雅地做到这一点吗?
答案 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>