我正在使用React 16.4.1和React-Router-Dom 4.3.1。我已经使用一个Web组件构建了一个侧边栏react组件,该组件将数据传递到该Web组件中,并呈现该组件。
这是侧边栏组件。
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import { Redirect } from 'react-router-dom';
class Sidebar extends React.Component {
constructor(props, context) {
super(props, context);
this.mySideBarRef = React.createRef();
this.menuClick = this.menuClick.bind(this);
this.state = {
redirect: false,
redirectUrl: '/',
menuData: [
{
groupName: 'Group 1',
icon: 'Home',
children: [
{
title: 'Home',
icon: 'favorite',
url: '/',
},
{
title: 'Grp 1 Item 2',
children: [
{
title: 'Roles',
icon: 'Person',
url: '/roles',
},
{
title: 'About',
icon: 'gesture',
url: '/about',
},
{
title: 'Google',
icon: 'world',
url: 'http://www.google.co.uk',
},
],
},
],
},
{
groupName: 'Group 2',
icon: 'search',
children: [
{
title: 'Grp 2 Item 1',
icon: 'microsoft',
children: [
{
title: 'Grp 2 Item 1.1 Bing',
icon: '',
url: 'http://www.google.co.uk',
},
{
title: 'Grp 2 Item 1.2 Bing',
icon: '',
url: 'http://www.bing.com',
},
],
},
],
},
],
};
}
componentDidMount() {
let sideMenuContainer = this.mySideBarRef.current;
sideMenuContainer.sideMenuData = this.state.menuData;
sideMenuContainer.overrideCustomUrlEvent = true;
window.addEventListener('customUrlEvent', this.menuClick)
}
menuClick(e) {
console.log(e);
this.setState({
redirect: true,
redirectUrl: e.url
});
}
componentWillUnmount() {
this.mySideBarRef.current.removeEventListener('customUrlEvent', this.menuClick);
}
render() {
if(this.state.redirect) {
this.state.redirect = false;
return <Redirect to={this.state.redirectUrl} />;
}
if(this.state.menuData !== null) {
return(
<side-bar-component
ref={this.mySideBarRef}
collapsable>
</side-bar-component>
);
}
}
}
export default Sidebar;
在加载侧栏组件时,它会正确呈现数据,一旦单击链接之一,它就会重定向页面,但不会加载数据,因为未触发componentDidMount。这是app.js
// This component handles the App template used on every page.
import React from 'react';
import PropTypes from 'prop-types';
import { Route } from 'react-router-dom';
import Header from './common/header';
import {connect} from 'react-redux';
import HomePage from './home/home-page';
import RolesPage from './role/roles-page';
import AboutPage from './about/about-page';
import { ManageRolePage } from "./role/manage-role-page";
import Sidebar from "./common/sidebar"; //eslint-disable-line import/no-named-as-default
class App extends React.Component {
render() {
return (
<div>
<Header
loading={this.props.loading}
/>
<div>
<div className="demo-sidebar-container">
<Sidebar />
</div>
<div className="demo-content">
<Route exact path="/" component={HomePage}/>
<Route path="/roles" component={RolesPage}/>
<Route path="/role/:id" component={ManageRolePage}/>
<Route path="/role" component={ManageRolePage} exact />
<Route path="/about" component={AboutPage}/>
</div>
</div>
</div>
);
}
}
App.propTypes = {
loading: PropTypes.bool.isRequired,
match: PropTypes.object.isRequired
};
function mapStateToProps(state, ownProps) {
return {
loading: state.ajaxCallsInProgress > 0
};
}
export default connect(mapStateToProps)(App);
对于新的反应,我认为我缺少一个基本步骤,我要实现的目标是进行重定向,还可以根据业务逻辑进行选择,以将数据重新加载到侧边菜单中或保留数据作为单击项将被展开,并希望保留侧边菜单数据。上面的示例中有一个硬编码的数据集,但最终我将获得一个可以参数化的服务。关于我要实现的目标的任何指导或示例将不胜感激。
**
阅读亚当斯的评论后,我进行了以下更改: 将所有代码放到sidebar-container.js和现在的sidebar.js中 看起来像这样;
**
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import { Redirect } from 'react-router-dom';
const Sidebar = React.forwardRef((props, ref) => (
<dijits-ui-dds-sidebar
ref={ref}
collapsable>
</dijits-ui-dds-sidebar>
));
export default Sidebar;
**
我的sidebar-container.js文件现在包含用于 控制侧栏:
**
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as sideMenuActions from '../../actions/side-bar-actions';
import Sidebar from "./sidebar";
import sideMenu from "../../reducers/side-menu-reducer";
import {Redirect} from "react-router-dom";
class SidebarContainer extends React.Component {
constructor(props, context) {
super(props, context);
this.mySideBarRef = React.createRef();
this.menuClick = this.menuClick.bind(this)
this.state = {
redirect: false,
redirectUrl: '/',
sideMenu: Object.assign({}, this.props.sideMenu),
};
}
componentDidMount() {
let sideMenuContainer = this.mySideBarRef.current;
if(this.state.sideMenu.menuData !== undefined) {
sideMenuContainer.sideMenuData = this.state.sideMenu.menuData;
}
sideMenuContainer.overrideCustomUrlEvent = true;
window.addEventListener('customUrlEvent', this.menuClick);
}
componentWillReceiveProps(nextProps) {
if (nextProps.sideMenu !== this.state.sideMenu) {
let sideMenuContainer = this.mySideBarRef.current;
if(nextProps.sideMenu.menuData !== undefined) {
sideMenuContainer.sideMenuData = nextProps.sideMenu.menuData;
}
}
}
componentWillUnmount() {
this.mySideBarRef.current.addEventListener('customUrlEvent', this.menuClick);
}
menuClick(e) {
console.log(e);
this.setState({
redirect: true,
redirectUrl: e.url
});
}
render() {
if(this.state.redirect) {
this.setState({ redirect: false} );
return <Redirect to={this.state.redirectUrl} />;
}
return (
<div>
<Sidebar sideMenu={this.props.sideMenu} ref={this.mySideBarRef}/>
</div>
);
}
}
SidebarContainer.propTypes = {
sideMenu: PropTypes.object.isRequired
};
function mapStateToProps(state, ownProps) {
debugger;
return {
sideMenu: state.sideMenu,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(sideMenuActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps) (SidebarContainer);
**
侧边栏容器位于我的app.js中。
**
import React from 'react';
import PropTypes from 'prop-types';
import { Route } from 'react-router-dom';
import Header from './common/header';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import HomePage from './home/home-page';
import RolesPage from './role/roles-page';
import AboutPage from './about/about-page';
import { ManageRolePage } from "./role/manage-role-page";
import SidebarContainer from "./common/sidebar-container"; //eslint-disable-line import/no-named-as-default
class App extends React.Component {
render() {
return (
<div>
<Header
loading={this.props.loading}
/>
<div>
<div className="sidebar-container">
<SidebarContainer sideMenu={this.props.sideMenu} />
</div>
<div className="content">
<Route exact path="/" component={HomePage}/>
<Route path="/roles" component={RolesPage}/>
<Route path="/role/:id" component={ManageRolePage}/>
<Route path="/role" component={ManageRolePage} exact />
<Route path="/about" component={AboutPage}/>
</div>
</div>
</div>
);
}
}
App.propTypes = {
loading: PropTypes.bool.isRequired,
sideMenu: PropTypes.object.isRequired,
match: PropTypes.object.isRequired
};
function mapStateToProps(state, ownProps) {
debugger;
return {
loading: state.ajaxCallsInProgress > 0,
sideMenu: state.sideMenu,
};
}
export default connect(mapStateToProps)(App);
**
但是我仍然遇到与单击以下项目时相同的问题 侧边菜单栏,将我路由到其他路线,该菜单会丢失 数据。我知道原因,但尝试为此寻求解决方案。 容器中的menuClick(e)函数设置了redirect属性 和redirectUrl状态值刷新状态并强制 要重新加载的父容器,我也在渲染中需要设置 重定向回false的状态。这导致父母 刷新容器,然后强制边栏刷新。一世 感觉我在反应中缺少某些东西或误解了一些东西 做事的方式。我也有一个动作来执行 侧栏的初始负载,看起来像componentDidMount 没有为子组件设置propType,所以我添加了 选择了propType的componentWillReceiveProps生命周期方法 变化。这是否意味着我不需要以下内容 在componentDidMount方法中排成一行?
**
if(this.state.sideMenu.menuData !== undefined) {
sideMenuContainer.sideMenuData = this.state.sideMenu.menuData;
}
**
对我的反应迟钝表示歉意,但我期待着您的光临 帮助。再次感谢社区的支持。
**
还包括我的route.js,以防可能需要解决此问题。
import React from 'react';
import { Route, IndexRoute, Switch } from 'react-router-dom';
// import { Route, IndexRoute } from 'react-router';
import App from './components/app';
import HomePage from './components/home/home-page';
import AboutPage from './components/about/about-page';
import RolesPage from './components/role/roles-page';
import ManageRolePage from './components/role/manage-role-page'; //eslint-disable-line import/no-named-as-default
export default (
<Switch>
<Route path="/" components={App}>
<IndexRoute component={HomePage} />
<Route path="roles" component={RolesPage}/>
<Route path="role" component={ManageRolePage}/>
<Route path="role/:id" component={ManageRolePage}/>
<Route path="about" component={AboutPage}/>
</Route>
</Switch>
);
再玩一会儿后,我将menuClick
事件更改为使用私有变量,而不是将状态变量用于redirectUrl或重定向标志。
menuClick(e) {
console.log(e);
this.redirectUrl = e.url;
this.redirect = true;
}
这表示状态没有被更新,但是我可以保留菜单扩展,但是页面不会重定向,因为我没有点击渲染功能事件。
答案 0 :(得分:2)
只要状态或道具发生变化,组件就会重新渲染,这意味着它们的render
方法和相关的生命周期方法将再次运行。
关于您的代码,我最注意到的是您正在使用Route
,但没有使用Switch
或BrowserRouter
。这可能是相关的。我不能肯定地说,但是由于您的<SideBar>
组件位于Routes的上游,因此除非状态或道具发生变化,否则它不会重新渲染或更新。
我认为您的侧边栏不会更新,数据最初可能会加载,但会过时。
但是请别担心,请允许我给您链接一篇我认为将带您进入更高层次的文章:https://medium.com/@pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf(请注意未来的个人:如果该URL将来会中断,请告诉我)>
我想您会在阅读完本书后弄清楚的,但是它将详细介绍BrowserRouter
,Switch
和Route
。我认为您需要更改代码以使用所有这三个代码。
请记住,补充工具栏(及其所有子项)仅在状态或道具更改时才会更新。
您可以执行以下操作:
class App extends Component {
constructor(props) {
super(props)
this.state = {
data: {},
}
}
render() {
return (
<div>
<Sidebar data={this.state.data} />
<AnotherComponent onEventOccurred={newData => this.setState({ data: newData })} />
</div>
)
}
}
在React中,我们将其称为提升状态,因此,补充工具栏的父级将是控制补充工具栏所依赖的状态的组件。在补充工具栏中,您可以将该状态称为this.props.data
(或者如果它是无状态功能组件,则称为props.data
)。每次道具更改时,侧边栏都会重新呈现自己。
其中的第二部分显示为:
<AnotherComponent onEventOccurred={newData => this.setState({ data: newData })} />
该组件具有一个名为onEventOccurred
的道具,可以从该组件内部将其称为this.props.onEventOccurred(newData)
,或者将功能非类组件称为props.onEventOccurred()
。它实际上只是一个回调,因此您将其调用并在其父级中触发this.setState()
。这是一种非常强大的模式,因为它允许父级控制将对该事件执行的操作,因为操作是在父级中定义的。子级仅触发回调。
起初我很犹豫要发布答案,但是现在我觉得您可能会从中学到很多。让我知道是否还有任何具体的不清楚的地方。我希望您阅读我首先链接的整个中型文章。其中显示的模式非常普遍且惯用。
我刚刚注意到您已安装Redux。总体而言,这确实增加了复杂性,但对于跨领域的关注而言却是一件好事。有时我们称其为横向数据加载。通常,数据在React中是单向流动的,也就是说总是从父级到子级,从上游到下游。
使用Redux,您可以从任何组件中调用动作创建者,并且补充工具栏将监听状态Object上的更改。当您希望状态在不直接彼此连接的多个组件之间共享状态时,可以使用Redux。
使用Redux,连接的组件正在侦听“基本上”全局状态树上的更改。当状态更改时,订阅的组件将更新。
例如在补充工具栏中,您可以这样做:
function mapStateToProps(state, ownProps) {
return {
loading: state.ajaxCallsInProgress > 0,
data: state.sidebar,
};
}
,并且每次this.props.data
更改时,它都会更新其state.sidebar
。我正在尝试将其保持在最小限度,因为我可能会将您与额外的信息混淆,但这是最重要的部分。使用connect()
的组件已订阅全局状态树上的更改。
Connect是一个高阶组件,使用它包装组件,因此,当应用程序状态更改时,它将导致该组件的道具发生更改,从而触发重新渲染。