[英]React-Redux: Dispatching on Constructor -> props.store.state not updated
我在React和Redux上都很陌生,我只用了大约2-3周来开发应用程序的alpha版本。
虽然大多数使用Redux和React的教程似乎都非常复杂,但我找到了一个允许我快速编写代码以在我的应用程序中尝试非常简单的场景的教程。
我现在面临的主要问题是:我想点击一个图像并在另一个页面上显示所述属性的详细信息(路由到使用react-router,在路径中传递id - 在当前代码中澄清我正在使用22的硬编码ID,而id尚未在路径中传递)。 我认为它会直接点击应用然后在构造函数或componentWillMount方法中我可以调用this.props.foo(id)然后使用this.props.store.foo获取属性但似乎商店当时没有更新。 但是,如果我在重定向之前在页面的handleClick方法中调用this.props.foo(id),那么它将起作用,但是在刷新时,存储将恢复为默认值并导致错误。
我只是想知道我是否只是采取了一种完全错误的做法......或者只是遗漏了一些东西。
代码可能太多了,只是让我知道我是否应该减少它...寻找的函数是:
handleImageClick() - > Results.js
constructor() - > BuyDetails.js代码:
Index.js
let state = {
results: [],
selectedState:{},
};
let reducer = (state, action) => {
console.log("in reducer" + action.type);
switch (action.type) {
case 'ADD_RESULTS':
console.log("in reducer add");
console.log("in reducer results = " + action.results);
var newState = Object.assign({}, state)
newState.results = action.results
console.log("in reducer add " + JSON.stringify(newState))
return newState
case 'GET_RESULTS':
console.log("in reducer get state = " + state.results[0].id);
var newState = Object.assign({}, state)
for (var result of state.results){
if (result.id === action.id){
console.log(result.img)
newState.selectedState = result
console.log(newState.selectedState.location.address)
}
}
console.log(newState.selectedState.location.address)
console.log(JSON.stringify(newState));
return newState
default:
return state
}
}
let store = createStore(reducer, state)
let mapStateToProps = state => ({
store: state
})
let mapDispatchToProps = dispatch => ({
addResults: (results) => dispatch({type: 'ADD_RESULTS', results:results}),
getSelectedResult: (id) => dispatch({type: 'GET_RESULTS', id:id}),
})
const ConnectedAppComponent = connect(
mapStateToProps, mapDispatchToProps
)(App)
const ConnectedResultsComponent = connect(
mapStateToProps, mapDispatchToProps
)(Results)
const ConnectedBuyDetailsComponent = connect(
mapStateToProps, mapDispatchToProps
)(BuyDetails)
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>
<Route path="/" component={ConnectedAppComponent}/>
{/* add the routes here */}
<Route path="/results" component={ConnectedResultsComponent}/>
<Route path="/buyDetails" component={ConnectedBuyDetailsComponent}/>
</Router>
</Provider>,
document.getElementById('root')
);
Results.js
class Results extends Component{
constructor(props) {
super(props);
this.state = {open: true, openProfile:false, anchorEl: null,dataSet:this.props.store.results};
console.log(this.state.dataSet.length)
console.log(this.state.dataSet[0].img)
}
handleTouchTap = (event) => {
// This prevents ghost click.
console.log("touch tap");
event.preventDefault();
const tempState = this.state;
tempState.openProfile = true
tempState.anchorEl = event.currentTarget
this.setState(tempState)
/*this.setState({
openProfile: true,
anchorEl: event.currentTarget,
});*/
};
handleRequestClose = () => {
const tempState = this.state;
tempState.openProfile = false
tempState.anchorEl = null
this.setState(tempState)
/*this.setState({
openProfile: false,
});*/
};
handleToggle = () => this.setState({open: !this.state.open});
handleImageClick(){
//This is where i could be doing this.props.getSelectedResult(22); and it would work but causes issues on refresh
const path = `/buyDetails`
this.context.router.push(path)
}
render() {
return <MuiThemeProvider>
<div className="Results" id="Results" style={styles}>
<div>
<Toolbar style={appBarStyle}>
<IconButton iconClassName="material-icons"
style={{bottom: '0',height:'auto'}}
onClick={this.handleToggle}>
menu
{/*<FontIcon className="material-icons" color={grey900} onClick={this.handleToggle}>menu</FontIcon>*/}
</IconButton>
<ToolbarGroup style={groupStyle}>
<ToolbarSeparator style={seperatorMargin}/>
<FontIcon style={searchIconnStyle} className="material-icons">search</FontIcon>
<ToolBarSearchField />
</ToolbarGroup>
<ToolbarGroup>
<ToolbarSeparator style={residentialSeperatorStyle}/>
<FlatButton label="Residential" style={selectedToolBarButtonStyle}/>
<ToolbarSeparator style={seperatorStyle}/>
<FlatButton label="Commerical" style={toolBarButtonStyle}/>
<ToolbarSeparator style={seperatorStyle}/>
<FlatButton label="JoellyR" style={toolBarButtonStyle} onTouchTap={this.handleTouchTap}/>
<Popover open={this.state.openProfile}
anchorEl={this.state.anchorEl}
anchorOrigin={{horizontal: 'right', vertical: 'bottom'}}
targetOrigin={{horizontal: 'right', vertical: 'top'}}
onRequestClose={this.handleRequestClose}>
<MenuItem value={1} primaryText="Price Range" />
<MenuItem value={2} primaryText="values" />
</Popover>
</ToolbarGroup>
</Toolbar>
<ToolBarFilterFields fieldNames={['Buy', 'Sell', 'Rent', 'Businesses', 'Mortgages']} displaySeperator={false}/>
</div>
<Drawer
open={this.state.open}
containerStyle={{top:'inherit', boxShadow:'(0,0,0,0)', border:'0px', borderRight:'1px solid', borderColor: 'rgba(0,0,0,0.3)'}}>
</Drawer>
<div style={this.state.open ? drawerExpanded : drawerCollapsed }>
<Paper style={paperStyle}>
<ToolBarFilterFields fieldNames={['Filters', 'Price', 'Bath', 'Beds', 'Type', 'Style']} displaySeperator={true}/>
<ResultGridList dataSet={this.state.dataSet} onClick = {() => this.handleImageClick()}/>
</Paper>
</div>
</div>
</MuiThemeProvider>
}
}
Results.contextTypes = {
router: React.PropTypes.object
}
export default Results;
BuyDetails.js
class BuyDetails extends Component{
constructor(props) {
super(props);
//dispatching the action here
this.props.getSelectedResult(22);
//getting the selected object from the props.state ... but it will still be = {}
this.state = {open: true, openProfile:false, anchorEl: null,data:this.props.store.selectedState};
}
componentWillMount() {
}
handleTouchTap = (event) => {
console.log('in buy detail: ' + JSON.stringify(this.props.store.selectedState) + JSON.stringify(this.props.store.results));
// This prevents ghost click.
console.log("touch tap2");
event.preventDefault();
const tempState = this.state;
tempState.openProfile = true
tempState.anchorEl = event.currentTarget
this.setState(tempState)
/*this.setState({
openProfile: true,
anchorEl: event.currentTarget,
});*/
};
handleRequestClose = () => {
const tempState = this.state;
tempState.openProfile = false
tempState.anchorEl = null
this.setState(tempState)
/*this.setState({
openProfile: false,
});*/
};
handleToggle = () => this.setState({open: !this.state.open});
render() {
return <MuiThemeProvider>
<div className="BuyDetails" id="BuyDetails" style={styles}>
<div>
<Toolbar style={appBarStyle}>
<IconButton iconClassName="material-icons"
style={{bottom: '0',height:'auto'}}
onClick={this.handleToggle}>
menu
{/*<FontIcon className="material-icons" color={grey900} onClick={this.handleToggle}>menu</FontIcon>*/}
</IconButton>
<ToolbarGroup style={groupStyle}>
<ToolbarSeparator style={seperatorMargin}/>
<FontIcon style={searchIconnStyle} className="material-icons">search</FontIcon>
<ToolBarSearchField />
</ToolbarGroup>
<ToolbarGroup>
<ToolbarSeparator style={residentialSeperatorStyle}/>
<FlatButton label="Residential" style={selectedToolBarButtonStyle}/>
<ToolbarSeparator style={seperatorStyle}/>
<FlatButton label="Commerical" style={toolBarButtonStyle}/>
<ToolbarSeparator style={seperatorStyle}/>
<FlatButton label="JoellyR" style={toolBarButtonStyle} onTouchTap={this.handleTouchTap}/>
<Popover open={this.state.openProfile}
anchorEl={this.state.anchorEl}
anchorOrigin={{horizontal: 'right', vertical: 'bottom'}}
targetOrigin={{horizontal: 'right', vertical: 'top'}}
onRequestClose={this.handleRequestClose}>
<MenuItem value={1} primaryText="Price Range" />
<MenuItem value={2} primaryText="values" />
</Popover>
</ToolbarGroup>
</Toolbar>
</div>
<Drawer
open={this.state.open}
containerStyle={{top:'inherit', boxShadow:'(0,0,0,0)', border:'0px', borderRight:'1px solid', borderColor: 'rgba(0,0,0,0.3)'}}>
</Drawer>
<div style={this.state.open ? drawerExpanded : drawerCollapsed }>
<Paper style={paperStyle}>
<BuyDetailGridList data={this.props.store.selectedState}/>
</Paper>
</div>
</div>
</MuiThemeProvider>
}
}
function isEmpty(obj) {
for(var key in obj) {
if(obj.hasOwnProperty(key))
return false;
}
return true;
}
export default BuyDetails;
谢谢大家...提前:)
+++更新 - 仍然没有工作+++
这是我尝试的另一种方法的代码,它只是在componentWillMount()中调用dispatch,然后直接将this.props.store.selectedState传递给子组件。
BuyDetails.js
class BuyDetails extends Component{
constructor(props) {
super(props);
this.state = {open: true, openProfile:false, anchorEl: null,data:{}};
//console.log('in buy details '+ JSON.stringify(this.state.data));
}
componentWillMount() {
//dispatching the action here... it is still this.props.store.selectedState is still = {}
this.props.getSelectedResult(22);
}
handleTouchTap = (event) => {
console.log('in buy detail: ' + JSON.stringify(this.props.store.selectedState) + JSON.stringify(this.props.store.results));
// This prevents ghost click.
console.log("touch tap2");
event.preventDefault();
const tempState = this.state;
tempState.openProfile = true
tempState.anchorEl = event.currentTarget
this.setState(tempState)
/*this.setState({
openProfile: true,
anchorEl: event.currentTarget,
});*/
};
handleRequestClose = () => {
const tempState = this.state;
tempState.openProfile = false
tempState.anchorEl = null
this.setState(tempState)
/*this.setState({
openProfile: false,
});*/
};
handleToggle = () => this.setState({open: !this.state.open});
render() {
return <MuiThemeProvider>
<div className="BuyDetails" id="BuyDetails" style={styles}>
<div>
<Toolbar style={appBarStyle}>
<IconButton iconClassName="material-icons"
style={{bottom: '0',height:'auto'}}
onClick={this.handleToggle}>
menu
{/*<FontIcon className="material-icons" color={grey900} onClick={this.handleToggle}>menu</FontIcon>*/}
</IconButton>
<ToolbarGroup style={groupStyle}>
<ToolbarSeparator style={seperatorMargin}/>
<FontIcon style={searchIconnStyle} className="material-icons">search</FontIcon>
<ToolBarSearchField />
</ToolbarGroup>
<ToolbarGroup>
<ToolbarSeparator style={residentialSeperatorStyle}/>
<FlatButton label="Residential" style={selectedToolBarButtonStyle}/>
<ToolbarSeparator style={seperatorStyle}/>
<FlatButton label="Commerical" style={toolBarButtonStyle}/>
<ToolbarSeparator style={seperatorStyle}/>
<FlatButton label="JoellyR" style={toolBarButtonStyle} onTouchTap={this.handleTouchTap}/>
<Popover open={this.state.openProfile}
anchorEl={this.state.anchorEl}
anchorOrigin={{horizontal: 'right', vertical: 'bottom'}}
targetOrigin={{horizontal: 'right', vertical: 'top'}}
onRequestClose={this.handleRequestClose}>
<MenuItem value={1} primaryText="Price Range" />
<MenuItem value={2} primaryText="values" />
</Popover>
</ToolbarGroup>
</Toolbar>
</div>
<Drawer
open={this.state.open}
containerStyle={{top:'inherit', boxShadow:'(0,0,0,0)', border:'0px', borderRight:'1px solid', borderColor: 'rgba(0,0,0,0.3)'}}>
</Drawer>
<div style={this.state.open ? drawerExpanded : drawerCollapsed }>
<Paper style={paperStyle}>
<BuyDetailGridList data={this.props.store.selectedState}/>
</Paper>
</div>
</div>
</MuiThemeProvider>
}
}
function isEmpty(obj) {
for(var key in obj) {
if(obj.hasOwnProperty(key))
return false;
}
return true;
}
export default BuyDetails;
我不会在详细信息组件中获取该项,至少不是显式的。
考虑:
详细信息组件:
class DetailsComponent extends React.Component {
// the item is now available in props.item
}
function mapStateToProps(state, props) {
return {
item: state.getSelectedItem()
};
}
export default connect(mapStateToProps)(DetailsComponent);
列表组件:
class ListComponent extends React.Component {
...
onImageClick = (item) => {
this.props.setSelectedItem(item);
}
...
}
这依赖于set
某些相关状态的set
/ getSelectedItem
操作。 详细信息组件将在安装时自动获取所选项目。
另一件需要考虑的事情是,如果两个组件同时呈现(例如,在列表/细节样式UI中),则将所选状态提升到父状态( 两个组件的父级)。
class ParentComponent extends React.Component {
...
onItemSelected = (item) => {
this.setState({ selectedItem: item });
}
render() {
return (
<ListComponent onItemSelected={ this.onItemSelected }/>
<DetailsComponent item={ this.state.selectedItem }/>
);
}
}
总而言之,你发布了很多代码,并且有点难以分辨出发生了什么。 希望我上面写的内容可以帮助解决您的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.