我很难用继承来代替React中的合成。我将基于React样式指南中不鼓励使用的继承,尝试说明所提出的问题和当前的解决方案。
首先,我在超级组件中定义通用状态和方法:
export default class SuperComponent extends Component {
constructor(props) {
super(props);
this.state = commonState;
}
foo() {
this.setState({a: 3});
}
render() {
return (
<div>
{this.getContent()}
</div>
)
}
}
然后,我使子组件具有更多的状态和方法。这些还应该可以访问超级组件的状态:
export default class SubComponent1 extends SuperComponent {
constructor(props) {
super(props);
this.state = Object.assign(this.state, subComponent1State);
}
bar() {
this.setState({b: 7});
}
getContent() {
return (
<div>
I'm subcomponent 1
</div>
)
}
}
export default class SubComponent2 extends SuperComponent {
constructor(props) {
super(props);
this.state = Object.assign(this.state, subComponent2State);
}
bar() {
this.setState({c: 1});
}
getContent() {
return (
<div>
I'm subcomponent 2
</div>
)
}
}
当我尝试将其转换为基于合成的方法时,会得到以下信息:
export default class SuperComponent extends Component {
foo() {
this.props.setStateMethod({a: 3});
}
render() {
return (
<div>
<div>
{this.props.text}
</div>
</div>
)
}
}
export default class SubComponent1 extends Component {
constructor(props) {
super(props);
this.state = Object.assign(commonState, subComponent1State);
}
bar() {
this.setState({b: 7});
}
render() {
return (
<SuperComponent
text={"I'm subcomponent 1"}
setStateMethod={this.setState}
subComponentState={this.state}
/>
)
}
}
export default class SubComponent2 extends Component {
constructor(props) {
super(props);
this.state = Object.assign(commonState, subComponent2State);
}
bar() {
this.setState({c: 1});
}
render() {
return (
<SuperComponent
text={"I'm subcomponent 2"}
setStateMethod={this.setState}
subComponentState={this.state}
/>
)
}
}
这是将基于继承的解决方案转换为基于组合的解决方案的好方法吗?因为共同状态和区分状态被更好地分开,基于继承的继承不是更好吗?在基于组合的解决方案中,必须在初始化时在每个子组件中定义公共状态。
答案 0 :(得分:1)
首先,您应该阅读React's Team response on the matter。
代码非常模糊,因此我无法确定您的案子是否真的很特殊,但是我对此表示怀疑,所以让我解决普遍的共识:
在真空中,继承和组合同样有用。在某些语言或项目中,您将更喜欢从中继承一个通用类,而不是要导入和调用的十二个函数,但是在React中,主要是因为它是以组件为中心的方法,相反。
合成使React仅保持两件事:props
和state
。您信任收到的内容,以可预测的方式采取行动,并在呈现的内容中同时反映props
和state
。如果您需要稍微改变渲染的内容,则可以发送不同的props
,甚至可以制作一个包装第一个用于特定用途的组件。该组件甚至可以自己拥有一些逻辑,然后将其传递给render props,以便您可以更精确地控制结果。如果需要使用此逻辑包装不同的组件,可以制作一个HOC并轻松包装任何组件。到那时,您可以制作数十个将不同事物返回到同一props
的组件,并且可以在组件库之间切换以显示数据并进行处理。
尽管诸如Render props或HOC之类的东西看起来是不同的东西,但它们只是使用已存在的组件逻辑并以允许您重用代码并在React中仍然有意义的方式应用它的技术。这是一个好的解决方案吗?好吧,它行得通,但是最终您将陷入困境,并弄混了几乎相同的二十个概念。这就是为什么现在有一个proposal用于范式更改的新功能,该功能介于继承和合成之间。但是,在React的做事方式中,构图仍然没有意义。
归根结底,这是解决同一问题的另一种方式。合成在React上工作得更好,并在render
中为您提供了更多控制权来混合和匹配您所需的内容。
同样,如果您有一个实际的例子,我可以使用不同的React技术为您提供更好的解释,但是对于您当前的方法,显然它没有好坏之分,因为它什么都不做。这需要花更多时间进行修改,而不仅仅是作出解释。
答案 1 :(得分:0)
在这种情况下,我认为您的解决方案不是基于合成的最佳解决方案。由于您希望子组件具有它们自己的状态(并且我假设它们将具有自己的逻辑),因此我将使用hoc,如下所示:
{
"input": {
"customClass": "input",
"customModule": "abcInput",
"customModuleProvider": "Whatever"
},
"button": {
"customClass": "ViewController",
"customModule": "Testing",
"customModuleProvider": "target"
}
}
var DOMParser = new (require('xmldom')).DOMParser({ normalizeTags: { default: false } });
var express = require("express"),
bodyParser = require('body-parser');
require("body-parser-xml")(bodyParser);
var xml2js = require('xml2js');
var builder = new xml2js.Builder({ standalone: { default: false } });
var app = express();
//Options of body-parser-xml module
app.use(bodyParser.xml({
xmlParseOptions: {
normalize: false, // Trim whitespace inside text nodes
normalizeTags: false, // Transform tags to lowercase
explicitArray: false // Only put nodes in array if >1
}
}));
//Post Method
app.post('/users', function (req, res, body) {
//Parsing Request.Body
var document = DOMParser.parseFromString(
builder.buildObject(req.body).toString()
);
//Getting a list of elements whose name is being given
var node = document.getElementsByTagName("TextView");
//Changing Tag Name of Specific Elements
for (var i = 0; i < node.length; i++) {
node[i].tagName = "com.mycompany.projectname.TextView";
}
//Getting a list of elements whose name is being given
var node = document.getElementsByTagName("com.example.usmanchattha.custom.TextView");
//Setting attributes
for (var i = 0; i < node.length; i++) {
node[i].setAttribute("android:id", "@+id / text2");
node[i].setAttribute("android:text", "Custom Android Font");
node[i].setAttribute("customfontdemo:chattha", "faizan");
node[i].setAttribute("android:padding", "12dp");
node[i].setAttribute("customfontdemo:fontName", "pipe_dream.ttf");
node[i].setAttribute("android:textSize", "32sp");
}
//Responsing Updated Data
res.send(document.toString());
});
app.listen(1000);
也将被包装:// hoc.js
const withSuper = (SubComponent) => {
class WithSuper extends React.Component {
constructor(props) {
super(props);
this.foo = this.foo.bind(this);
}
foo() { // could also receive a value
this.props.setState({ a: 3 });
}
render() {
// Passes to the SubComponent:
// the state.a as a prop
// function foo to modify state.a
// the rest of the props that may be passed from above
return (
<SubComponent
a={this.state.a}
foo={this.foo}
{...this.props}
/>
)
}
}
return WithSuper;
};
// SubComponent1.js
class SubComponent1 extends Component {
constructor(props) {
super(props);
this.state = { b: 0 }; // or anything else
}
bar() {
this.setState({b: 7});
}
render() {
// to access the 'a' value use: this.props.a
// to modify the 'a' value call: this.props.foo()
return (
<div>
I'm subcomponent 1
</div>
);
}
}
export default withSuper(SubComponent1);
,因此它也可以访问SubComponent2
和withSuper(SubComponent2)
道具。
我认为这种解决方案更好,因为特殊的“超级”封装了a
的行为,并且子组件只需要担心修改其特定状态。