有条件地加载 Reactjs 组件时减少代码冗余
Reduce code redundancy when conditionally loading Reactjs components
我正在尝试在类父组件中加载不同的 React 组件 <Block1>
。
我想要实现的是,当我按下 "next" 按钮 <Button1>
时,<Component2>
必须加载到 <Block1>
和 "next" 按钮应更改为 "previous",但单击 "previous" <Button2>
必须加载 <Component1>
。
我不想使用 <Block2>
。如您所见,我在这里写了很多冗余代码。有没有办法仅在单击按钮时更改 <Block1>
中的组件?
var Block1 = React.createClass({
render: function(){
return(
<div className="block">
<Component1/>
<Button1/>
</div>
)
}
});
var Component1 = React.createClass({
render: function(){
return(
<p className = "component1">This is Component1</p>
)
}
});
var Button1 = React.createClass({
next: function(){
ReactDOM.render(<Block2/>, document.getElementById("container"));
},
render: function(){
return(
<button className = "button1" onClick = {this.next}>Next</button>
)
}
});
var Block2 = React.createClass({
render: function(){
return(
<div className="block">
<Component2/>
<Button2/>
</div>
)
}
});
var Component2 = React.createClass({
render: function(){
return(
<p className = "component2">This is Component2</p>
)
}
});
var Button2 = React.createClass({
previous: function(){
ReactDOM.render(<Block1/>, document.getElementById("container"));
},
render: function(){
return(
<button className = "button1" onClick = {this.previous}>previous</button>
)
}
});
ReactDOM.render(<Block1/>, document.getElementById("container"));
Is there a way to change the components inside <Block1>
only on button click?
是的。
解决这个问题的一种方法是让主要的顶级组件 <Block1>
,或者我们称之为 <MainBlock>
,是一个有状态的组件,它维护状态并驱动决定渲染内容的逻辑.其他组件可能只是根据 <MainBlock>
设置的状态标志呈现的无状态表示组件。
本质上,<MainBlock>
有一个状态标志,比方说 isShowingNext
,它用于确定呈现哪个部分("next" 或 "previous"),以及标志由按钮调用的操作设置。
就按钮本身而言,它们附加了 onClick 侦听器,每当单击时,都会在 <MainBlock>
中调用一个操作,并且 <MainBlock>
会更新状态标志并重新呈现适当的组件。
由于按钮几乎相同,我们可以将它们组合成一个按钮组件,例如 <ToggleButton>
,它将有条件地呈现("previous" 或 "next"),具体取决于通过的标志(参见 第一种方法)。
根据您的使用情况,如果按钮(及其渲染逻辑)不太相似,您可以有两个按钮,例如 <ButtonPrev>
和 <ButtonNext>
,其中一个或另一个根据状态标志由 <MainBlock>
渲染(参见 第二种方法 )。
第一种方法
jsFiddle sample
var MainBlock = React.createClass({
// initially we are showing the "previous" page
getInitialState() {
return {
isShowingNext: false
};
},
// action that updates the state flag to determine what gets rendered
handleUpdateIsShowingNext: function(isShowingNext) {
this.setState({
isShowingNext: isShowingNext
});
},
// render function that displays the proper template based on the state flag
render: function(){
var isShowingNext = this.state.isShowingNext;
return (
<div className="block">
{isShowingNext ? <ComponentNext/> : <ComponentPrev/>}
<ToggleButton isShowingNext={isShowingNext}
onUpdateIsShowingNext={this.handleUpdateIsShowingNext}
/>
</div>
);
}
});
// button for that toggles between "previous" or "next" page depending on the logic
var ToggleButton = React.createClass({
toggle: function(){
this.props.onUpdateIsShowingNext(!this.props.isShowingNext); // toggle the "isShowingNext" flag
},
render: function(){
var isShowingNext = this.props.isShowingNext;
if (isShowingNext) {
return <button className="button-prev" onClick={this.toggle}>Prev</button>;
} else {
return <button className="button-next" onClick={this.toggle}>Next</button>;
}
}
});
// representational component for the "previous" page
var ComponentPrev = React.createClass({
render: function(){
return <p className = "component-prev">This is ComponentPrev</p>;
}
});
// representational component for the "next" page
var ComponentNext = React.createClass({
render: function(){
return <p className = "component-next">This is ComponentNext</p>;
}
});
ReactDOM.render(<MainBlock/>, document.getElementById("container"));
第二种方法
jsFiddle sample
var MainBlock = React.createClass({
// initially we are showing the "previous" page
getInitialState() {
return {
isShowingNext: false
};
},
// returns the template for the "previous" page
getTemplateForPrev: function() {
return (
<div className="block">
<ComponentPrev/>
<ButtonPrev onUpdateIsShowingNext={this.handleUpdateIsShowingNext}/>
</div>
);
},
// returns the template for the "next" page
getTemplateForNext: function() {
return (
<div className="block">
<ComponentNext/>
<ButtonNext onUpdateIsShowingNext={this.handleUpdateIsShowingNext}/>
</div>
);
},
// action that updates the state flag to determine what gets rendered
handleUpdateIsShowingNext: function(isShowingNext) {
this.setState({
isShowingNext: isShowingNext
});
},
// render function that displays the proper template based on the state flag
render: function(){
return this.state.isShowingNext ? this.getTemplateForNext() : this.getTemplateForPrev();
}
});
// representational component for the "previous" page
var ComponentPrev = React.createClass({
render: function(){
return <p className = "component-prev">This is ComponentPrev</p>;
}
});
// button for the "previous" page
var ButtonPrev = React.createClass({
next: function(){
this.props.onUpdateIsShowingNext(true); // clicking "previous" makes isShowingNext be true
},
render: function(){
return <button className="button-prev" onClick={this.next}>Next</button>;
}
});
// representational component for the "next" page
var ComponentNext = React.createClass({
render: function(){
return <p className = "component-next">This is ComponentNext</p>;
}
});
// button for the "next" page
var ButtonNext = React.createClass({
previous: function(){
this.props.onUpdateIsShowingNext(false); // clicking "previous" makes isShowingNext be false
},
render: function(){
return <button className="button-next" onClick={this.previous}>previous</button>;
}
});
ReactDOM.render(<MainBlock/>, document.getElementById("container"));
您绝对不想在组件结构中的其他任何地方使用 ReactDOM.render
。
看起来您正在通过几个不同的步骤创建 "wizard"。
我编辑了你的fiddle:https://jsfiddle.net/guq81pa9/1/
顶级组件会跟踪您正在使用的'step',并处理来回切换:
var Block = React.createClass({
getInitialState: function(){
return {
step: 1
}
},
goto: function(step){
this.setState({
step: step || 1 // default to step 1
})
},
render: function(){
var renderStep = <div className="block component1">
<Component1/>
<Button onClick={this.goto.bind(this,2)}>Next</Button>
</div>
if (this.state.step === 2) {
renderStep = <div className="block component2">
<Component2/>
<Button onClick={this.goto.bind(this,1)}>Previous</Button>
</div>
}
return renderStep
}
});
我正在尝试在类父组件中加载不同的 React 组件 <Block1>
。
我想要实现的是,当我按下 "next" 按钮 <Button1>
时,<Component2>
必须加载到 <Block1>
和 "next" 按钮应更改为 "previous",但单击 "previous" <Button2>
必须加载 <Component1>
。
我不想使用 <Block2>
。如您所见,我在这里写了很多冗余代码。有没有办法仅在单击按钮时更改 <Block1>
中的组件?
var Block1 = React.createClass({
render: function(){
return(
<div className="block">
<Component1/>
<Button1/>
</div>
)
}
});
var Component1 = React.createClass({
render: function(){
return(
<p className = "component1">This is Component1</p>
)
}
});
var Button1 = React.createClass({
next: function(){
ReactDOM.render(<Block2/>, document.getElementById("container"));
},
render: function(){
return(
<button className = "button1" onClick = {this.next}>Next</button>
)
}
});
var Block2 = React.createClass({
render: function(){
return(
<div className="block">
<Component2/>
<Button2/>
</div>
)
}
});
var Component2 = React.createClass({
render: function(){
return(
<p className = "component2">This is Component2</p>
)
}
});
var Button2 = React.createClass({
previous: function(){
ReactDOM.render(<Block1/>, document.getElementById("container"));
},
render: function(){
return(
<button className = "button1" onClick = {this.previous}>previous</button>
)
}
});
ReactDOM.render(<Block1/>, document.getElementById("container"));
Is there a way to change the components inside
<Block1>
only on button click?
是的。
解决这个问题的一种方法是让主要的顶级组件 <Block1>
,或者我们称之为 <MainBlock>
,是一个有状态的组件,它维护状态并驱动决定渲染内容的逻辑.其他组件可能只是根据 <MainBlock>
设置的状态标志呈现的无状态表示组件。
本质上,<MainBlock>
有一个状态标志,比方说 isShowingNext
,它用于确定呈现哪个部分("next" 或 "previous"),以及标志由按钮调用的操作设置。
就按钮本身而言,它们附加了 onClick 侦听器,每当单击时,都会在 <MainBlock>
中调用一个操作,并且 <MainBlock>
会更新状态标志并重新呈现适当的组件。
由于按钮几乎相同,我们可以将它们组合成一个按钮组件,例如 <ToggleButton>
,它将有条件地呈现("previous" 或 "next"),具体取决于通过的标志(参见 第一种方法)。
根据您的使用情况,如果按钮(及其渲染逻辑)不太相似,您可以有两个按钮,例如 <ButtonPrev>
和 <ButtonNext>
,其中一个或另一个根据状态标志由 <MainBlock>
渲染(参见 第二种方法 )。
第一种方法
jsFiddle samplevar MainBlock = React.createClass({
// initially we are showing the "previous" page
getInitialState() {
return {
isShowingNext: false
};
},
// action that updates the state flag to determine what gets rendered
handleUpdateIsShowingNext: function(isShowingNext) {
this.setState({
isShowingNext: isShowingNext
});
},
// render function that displays the proper template based on the state flag
render: function(){
var isShowingNext = this.state.isShowingNext;
return (
<div className="block">
{isShowingNext ? <ComponentNext/> : <ComponentPrev/>}
<ToggleButton isShowingNext={isShowingNext}
onUpdateIsShowingNext={this.handleUpdateIsShowingNext}
/>
</div>
);
}
});
// button for that toggles between "previous" or "next" page depending on the logic
var ToggleButton = React.createClass({
toggle: function(){
this.props.onUpdateIsShowingNext(!this.props.isShowingNext); // toggle the "isShowingNext" flag
},
render: function(){
var isShowingNext = this.props.isShowingNext;
if (isShowingNext) {
return <button className="button-prev" onClick={this.toggle}>Prev</button>;
} else {
return <button className="button-next" onClick={this.toggle}>Next</button>;
}
}
});
// representational component for the "previous" page
var ComponentPrev = React.createClass({
render: function(){
return <p className = "component-prev">This is ComponentPrev</p>;
}
});
// representational component for the "next" page
var ComponentNext = React.createClass({
render: function(){
return <p className = "component-next">This is ComponentNext</p>;
}
});
ReactDOM.render(<MainBlock/>, document.getElementById("container"));
第二种方法
jsFiddle samplevar MainBlock = React.createClass({
// initially we are showing the "previous" page
getInitialState() {
return {
isShowingNext: false
};
},
// returns the template for the "previous" page
getTemplateForPrev: function() {
return (
<div className="block">
<ComponentPrev/>
<ButtonPrev onUpdateIsShowingNext={this.handleUpdateIsShowingNext}/>
</div>
);
},
// returns the template for the "next" page
getTemplateForNext: function() {
return (
<div className="block">
<ComponentNext/>
<ButtonNext onUpdateIsShowingNext={this.handleUpdateIsShowingNext}/>
</div>
);
},
// action that updates the state flag to determine what gets rendered
handleUpdateIsShowingNext: function(isShowingNext) {
this.setState({
isShowingNext: isShowingNext
});
},
// render function that displays the proper template based on the state flag
render: function(){
return this.state.isShowingNext ? this.getTemplateForNext() : this.getTemplateForPrev();
}
});
// representational component for the "previous" page
var ComponentPrev = React.createClass({
render: function(){
return <p className = "component-prev">This is ComponentPrev</p>;
}
});
// button for the "previous" page
var ButtonPrev = React.createClass({
next: function(){
this.props.onUpdateIsShowingNext(true); // clicking "previous" makes isShowingNext be true
},
render: function(){
return <button className="button-prev" onClick={this.next}>Next</button>;
}
});
// representational component for the "next" page
var ComponentNext = React.createClass({
render: function(){
return <p className = "component-next">This is ComponentNext</p>;
}
});
// button for the "next" page
var ButtonNext = React.createClass({
previous: function(){
this.props.onUpdateIsShowingNext(false); // clicking "previous" makes isShowingNext be false
},
render: function(){
return <button className="button-next" onClick={this.previous}>previous</button>;
}
});
ReactDOM.render(<MainBlock/>, document.getElementById("container"));
您绝对不想在组件结构中的其他任何地方使用 ReactDOM.render
。
看起来您正在通过几个不同的步骤创建 "wizard"。
我编辑了你的fiddle:https://jsfiddle.net/guq81pa9/1/
顶级组件会跟踪您正在使用的'step',并处理来回切换:
var Block = React.createClass({
getInitialState: function(){
return {
step: 1
}
},
goto: function(step){
this.setState({
step: step || 1 // default to step 1
})
},
render: function(){
var renderStep = <div className="block component1">
<Component1/>
<Button onClick={this.goto.bind(this,2)}>Next</Button>
</div>
if (this.state.step === 2) {
renderStep = <div className="block component2">
<Component2/>
<Button onClick={this.goto.bind(this,1)}>Previous</Button>
</div>
}
return renderStep
}
});