我有一个具有以下react-router和react-router-relay设置的网站:
main.jsx:
<Router history={browserHistory} render={applyRouterMiddleware(useRelay)}>
<Route path="/:classroom_id" component={mainView} queries={ClassroomQueries}>
<IndexRoute component={Start} queries={ClassroomQueries} />
<Route path="tasks" component={TaskRoot} queries={ClassroomQueries}>
<IndexRoute component={TaskList} queries={ClassroomQueries} />
<Route path="task/:task_id" component={TaskView} queries={ClassroomTaskQueries} />
<Route path="task/:task_id/edit" component={TaskEdit} queries={ClassroomTaskQueries} />
</Route>
</Route>
</Router>该网站是一个虚拟教室,教师可以在其中为学生创建任务。这些任务也可以编辑。
当用户在TaskEdit中编辑任务并点击保存时,将应用更改并将用户重定向到TaskList。现在我想给用户一个“您的更改被保存了!”一旦任务被编辑,就会收到消息。
为此,我创建了一个引导警报组件(<Alert>)。
为了说明起见,假设我有以下mainView组件(请参阅上面的路由器):
mainView.js:
render() {
<div>
<Menu />
<Row>
<Col md={3}>
<Sidebar />
</Col>
<Col md={9}>
{this.props.children} <-- Start, TaskRoot, etc go here
</Col>
</Row>
<Alert someProps={...} /> <-- Popup component
</div>
}通常,我会在active组件之外创建这个警报组件,然后传递某种函数,将警报显示为活动组件的支柱。类似于所示的这里。
但是,这只适用于特定的组件。但是,由于<Alert>位于页面的根级(以及菜单栏、侧边栏和其他所有页面上都是静态的东西),所以由于{this.props.children}的原因,我不能将其作为支柱传递。
请允许我再一次说明我想达到的目标:
<TaskEdit>中的任务中。<TaskList>。<Alert>触发,并显示一条消息。我见过其他解决方案,建议您可以克隆一个React元素的所有子元素,并应用打开alert所需的道具,但是由于react-router的性质,这看起来并不有效。
我怎样才能完成上述工作?是否有一种方法可以让组件在全球范围内访问?请注意,我不想在每个组件中重新创建<Alert>。整个DOM只有一个这样的组件。
发布于 2016-04-28 13:06:38
简单答案:
将Alert组件放在顶层视图中,然后使用一些pubsub或事件总线库来更新它。
在警报组件onComponentDidMount函数中,您侦听特定事件和数据,并相应地更新其状态(可见性和消息)
在任务保存函数中,将事件发布到事件总线。
更多提前回答:
发布于 2016-04-28 13:29:49
一个简单的解决方案是使用功能
const MainView = React.createClass({
childContextTypes: {
triggerAlert: PropTypes.func.isRequired,
},
getChildContext() {
return {
triggerAlert: alert => this.setState({ alert }),
};
},
getInitialState() {
return {
alert: null,
};
},
render() {
return (
<div>
{this.props.children}
<Alert alert={this.state.alert} />
</div>
};
},
});
const AnyChild = React.createClass({
contextTypes: {
triggerAlert: PropTypes.func.isRequired,
},
handleClick() {
this.context.triggerAlert('Warning: you clicked!');
},
render() {
return <button onClick={this.handleClick} />
},
});但是,这与命令式API相关联。这也意味着所有产生警报的子组件都必须重新实现一些样板。
您还可以将与Alert呈现相关的所有内容移动到它们自己的组件中,将您的实现隐藏在一个更简单的API中:
const AlertRenderer = React.createClass({
childContextTypes: {
addAlert: PropTypes.func.isRequired,
},
getChildContext() {
return {
addAlert: this.addAlert,
};
},
getInitialState() {
return {
alerts: [],
};
},
addAlert(alert) {
this.setState(({ alerts }) =>
({ alerts: alerts.concat([alert]) })
);
const remove = () => {
this.setState(({ alerts }) =>
({ alerts: alerts.splice(alerts.indexOf(alert), 1) })
);
};
const update = () => {
this.setState(({ alerts }) =>
({ alerts: alerts.splice(alerts.indexOf(alert), 1, alert) })
);
};
return { remove, update };
},
render() {
return (
<div>
{this.props.children}
{this.state.alerts.map(alert =>
<this.props.component
key={alert} // Or whatever uniquely describes an alert
alert={alert}
/>
)}
</div>
);
},
});
const Alert = React.createClass({
contextTypes: {
addAlert: PropTypes.func.isRequired,
},
propTypes: {
alert: PropTypes.any.isRequired,
},
componentWillMount() {
const { update, remove } = this.context.addAlert(this.props.alert);
this.updateAlert = update;
this.removeAlert = remove;
},
componentWillUnmount() {
this.removeAlert();
},
componentWillReceiveProps(nextProps) {
this.updateAlert(nextProps.alert);
},
render() {
return null;
},
});然后,应用程序的代码变成:
const AnyChild = React.createClass({
getInitialState() {
return {
alert: null,
};
},
handleClick() {
this.setState({
alert: 'Warning: you clicked the wrong button!',
});
},
render() {
return (
<div>
<button onClick={this.handleClick}>
</button>
{this.state.alert &&
<Alert alert={this.state.alert} />
}
</div>
);
},
});
const MainView = React.createClass({
render() {
return (
<AlertRenderer component={MyAlertComponent}>
{this.props.children}
</AlertRenderer>
);
},
});这种特殊方法的一个问题是,每当更新警报时,AlertRenderer都会重新呈现它的所有子级。这可能导致无限递归,因为AlertSub的componentWillReceiveProps生命周期再次触发。请注意,只有当组件有可更改的道具且不实现shouldComponentUpdate时,才会出现此问题。
解决这个问题的一个简单方法是创建一个有条件的子呈现器,只有当它检测到其子代已经更改时才会更新。
下面的示例代码为单个子程序创建了这样一个条件呈现器。为了有条件地呈现多个子组件,需要将其包装到一个额外的DOM组件中。
const ShouldSingleChildUpdate = React.createClass({
shouldComponentUpdate(nextProps) {
return nextProps.children !== this.props.children;
},
render() {
return this.props.children;
},
});
const AlertRenderer = React.createClass({
/* ... */
render() {
return (
<div>
<ShouldSingleChildUpdate>
{this.props.children}
</ShouldSingleChildUpdate>
{this.state.alerts.map(alert =>
<this.props.component
key={alert} // Or whatever uniquely describes an alert
alert={alert}
/>
)}
</div>
);
},
});注意,这种方法被认为适用于任何类型的调制解调器,其中可以在任何时候在屏幕上显示多个调制解调器。在您的情况下,由于任何时候屏幕上都应该只有一个Alert组件,所以您可以通过只在状态中存储一个警报来简化AlertRenderer代码。
https://stackoverflow.com/questions/36833463
复制相似问题