React-Redux:在构造函数上调度 -> props.store.state 未更新

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),那么它将起作用,但在刷新时,商店恢复为默认值并导致错误。

我只是想知道我是否只是采取了完全错误的方法..或者只是遗漏了什么。

代码可能太多了,如果我应该 trim 把它写下来,请告诉我... 要查找的函数是:

handleImageClick() -> Results.js

构造函数() -> 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() 中调用调度,然后将 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/getSelectedItem 操作。详情组件挂载时会自动抓取选中的item

另一件要考虑的事情是,如果同时呈现两个组件(例如 list/detail 样式 UI),则将选定状态提升到父级状态(两个组件的父级)。

class ParentComponent extends React.Component {
    ...

    onItemSelected = (item) => {
        this.setState({ selectedItem: item });
    }

    render() {
        return (
            <ListComponent onItemSelected={ this.onItemSelected }/>
            <DetailsComponent item={ this.state.selectedItem }/>
        );
    }
}

总而言之,您发布了很多代码,有点难以判断发生了什么。希望我上面写的内容对您的问题有所帮助。