React的新功能。只使用create-react-app和Material UI,没有别的。 来自Angular背景。
我无法从兄弟组件进行通信以打开侧边栏。 我将每个部分分成他们自己的文件。 我可以在Header中打开按钮与父App交谈,但无法让父App与孩子LeftSidebar通信。
String.Join(",", set.Ins.Select((input, i) => input + set.Outs.Skip(i).First()));
import React, {Component} from 'react';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationMenu from 'material-ui/svg-icons/navigation/menu';
class Header extends Component {
openLeftBar = () => {
// calls parent method
this.props.onOpenLeftBar();
}
render() {
return (
<AppBar iconElementLeft={
<IconButton onClick={this.openLeftBar}>
<NavigationMenu />
</IconButton>
}
/>
);
}
}
export default Header;
import React, { Component } from 'react';
import darkBaseTheme from 'material-ui/styles/baseThemes/darkBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import RaisedButton from 'material-ui/RaisedButton';
import Drawer from 'material-ui/Drawer';
import MenuItem from 'material-ui/MenuItem';
// components
import Header from './Header/Header';
import Body from './Body/Body';
import Footer from './Footer/Footer';
import LeftSidebar from './LeftSidebar/LeftSidebar';
class App extends Component {
constructor() {
super() // gives component context of this instead of parent this
this.state = {
leftBarOpen : false
}
}
notifyOpen = () => {
console.log('opened') // works
this.setState({leftBarOpen: true});
/*** need to pass down to child component and $watch somehow... ***/
}
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<Header onOpenLeftBar={this.notifyOpen} />
<Body />
<LeftSidebar listenForOpen={this.state.leftBarOpen} />
<Footer />
</div>
</MuiThemeProvider>
);
}
}
export default App;
答案 0 :(得分:2)
放开像“观察者”这样的概念。在React中,只有state
和props
。当组件的状态通过this.setState(..)
更改时,它将更新render
中的所有子项。
您的代码遭受了重复状态的典型反模式。如果标题和兄弟组件都想要访问或更新同一个状态,那么它们属于一个共同的祖先(在你的情况下是App),而不是其他地方。
(为简洁起见,删除/重命名了一些内容)
class App extends Component {
// don't need `constructor` can just apply initial state here
state = { leftBarOpen: false }
// probably want 'toggle', but for demo purposes, have two methods
open = () => {
this.setState({ leftBarOpen: true })
}
close = () => {
this.setState({ leftBarOpen: false })
}
render() {
return (
<div className="App">
<Header onOpenLeftBar={this.open} />
<LeftSidebar
closeLeftBar={this.close}
leftBarOpen={this.state.leftBarOpen}
/>
</div>
)
}
}
现在Header和LeftSidebar根本不需要成为类,只需对props做出反应,并调用prop函数。
const LeftSideBar = props => (
<Drawer open={props.leftBarOpen}>
<IconButton onClick={props.closeLeftBar}>
<NavigationClose />
</IconButton>
</Drawer>
)
现在,只要App
中的状态发生变化,无论是谁发起了变更,你的LeftSideBar都会做出适当的反应,因为它只知道最新的道具
答案 1 :(得分:1)
将leftBarOpen
道具设置为LeftNavBar
的内部状态后,您无法再在外部对其进行修改,因为您只读取constructor
中仅在组件运行一次时的道具自我初始化。
您可以使用componentWillReceiveProps life cycle method并在收到新道具时分别更新状态。
话虽如此,我认为抽屉不应该对被关闭或开放负责,但应该对其关闭或打开时的外观或内容负责。
抽屉不能自行关闭或打开,与轻型球相同无法打开或关闭球,但开关/按钮可以而且应该。
这是一个小例子来说明我的观点:
const LightBall = ({ on }) => {
return (
<div>{`The light is ${on ? 'On' : 'Off'}`}</div>
);
}
const MySwitch = ({ onClick, on }) => {
return (
<button onClick={onClick}>{`Turn the light ${!on ? 'On' : 'Off'}`}</button>
)
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
lightOn: false
};
}
toggleLight = () => this.setState({ lightOn: !this.state.lightOn });
render() {
const { lightOn } = this.state;
return (
<div>
<MySwitch onClick={this.toggleLight} on={lightOn} />
<LightBall on={lightOn} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>