I would like to ask more experienced coleagues for help in understanding below piece of code and make it working in my App.
The general goal is to invoke an REDUX action from my button, which will delete an item from a database.
Here is a piece of code that works for me:
<MenuItem onClick={this.props.deleteTrip.bind(this, trip.id)}>
deleteTrip
</MenuItem>
and this code is a part of this file:
class Trips extends Component {
state = {
anchorEl: null,
dialogOpen: false
};
//integracja z reduxem
static propTypes = {
trips: PropTypes.array.isRequired
};
//
handleClose = () => {
this.setState({
anchorEl: null
});
};
handleClick = event => {
this.setState({
anchorEl: event.currentTarget
});
};
render() {
const { label, status, trips } = this.props;
const { anchorEl, dialogOpen } = this.state;
const TripsFiltered = [];
return (
<Fragment>
<List>
<ListItemLink>
<ListItemAvatar>
<Avatar>
<WorkIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={trip.remarks}
secondary={
trip.startDate + "-" + trip.endDate + ", " + trip.place
}
/>
<ListItemSecondaryAction>
<IconButton
edge="end"
aria-label="more"
aria-controls="simple-menu"
aria-haspopup="true"
onClick={this.handleClick}
>
<MoreIcon />
</IconButton>
<Menu
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={this.handleClose}
>
<MenuItem onClick={this.handleClose}>editTrip</MenuItem>
<MenuItem
onClick={this.props.deleteTrip.bind(this, trip.id)}>
deleteTrip
</MenuItem>
</Menu>
</ListItemSecondaryAction>
</ListItemLink>
</List>
</Fragment>
);
}
}
const mapStateToProps = state => ({
trips: state.tripsReducer.trips
});
export default connect(
mapStateToProps,
{ deleteTrip }
)(Trips);
But the case for me is that before deleting a Trip from database I want to show to the user a warning message so I modified the code like that:
class Trips extends Component {
state = {
anchorEl: null,
dialogOpen: false
};
//integracja z reduxem
static propTypes = {
trips: PropTypes.array.isRequired
};
//
handleOpenWarning = () => {
this.setState({
dialogOpen: true
});
};
handleCloseWarning = () => {
this.setState({
dialogOpen: false
});
};
handleDeleteTrip = () => {
this.setState({
dialogOpen: false
});
this.props.deleteTrip.bind(this, trip.id);
};
handleClose = () => {
this.setState({
anchorEl: null
});
};
handleClick = event => {
this.setState({
anchorEl: event.currentTarget
});
};
render() {
const { label, status, trips } = this.props;
const { anchorEl, dialogOpen } = this.state;
const TripsFiltered = [];
return (
<Fragment>
<List>
<ListItemLink>
<ListItemAvatar>
<Avatar>
<WorkIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={trip.remarks}
secondary={
trip.startDate + "-" + trip.endDate + ", " + trip.place
}
/>
<ListItemSecondaryAction>
<IconButton
edge="end"
aria-label="more"
aria-controls="simple-menu"
aria-haspopup="true"
onClick={this.handleClick}
>
<MoreIcon />
</IconButton>
<Menu
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={this.handleClose}
>
<MenuItem onClick={this.handleClose}>editTrip</MenuItem>
<MenuItem
onClick={this.handleOpenWarning}
>
deleteTrip
</MenuItem>
</Menu>
</ListItemSecondaryAction>
</ListItemLink>
</List>
<Dialog
open={dialogOpen}
onClose={this.handleCloseWarning}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Selected Trip will be deleted."}
</DialogTitle>
<DialogActions>
<Button onClick={this.handleCloseWarning} color="primary">
Cancel
</Button>
<Button onClick={this.handleDeleteTrip} color="primary" autoFocus>
Delete
</Button>
</DialogActions>
</Dialog>
</Fragment>
);
}
}
const mapStateToProps = state => ({
trips: state.tripsReducer.trips
});
export default connect(
mapStateToProps,
{ deleteTrip }
)(Trips);
So after that modification I call {this.props.deleteTrip.bind(this, trip.id)} from handleDeleteTrip() function instead from onClick event directly. But when I do that I got:
./src/Trips/Trips.js
Line 57: 'trip' is not defined no-undef
In fact I dont understand to much this piece of code
{this.props.deleteTrip.bind(this, trip.id)}, so Im not sure how to properly use it in my arrow function to get this working.
in handleDeleteTrip(). there is no definition of the trip
handleDeleteTrip = () => {
this.setState({
dialogOpen: false
});
this.props.deleteTrip.bind(this, trip.id);
};
so you should pass the trip to the argument.
like this
handleDeleteTrip = (trip) => {
this.setState({
dialogOpen: false
});
this.props.deleteTrip.bind(this, trip.id);
};
...
<Button onClick={(e) => this.handleDeleteTrip(trip)} color="primary" autoFocus>
Delete
</Button>
Related
I have this code which his basic todo list that I am trying to build in react
const Home = (props) => {
const classes = useStyles();
const [addNewTask, setAddNewTask] = useState(false);
const [formData, setFormData] = React.useState({ taskText: "" });
const dispatch = useDispatch();
useEffect(() => {
dispatch(getTasks());
}, [dispatch]);
const todos = useSelector((state) => state.todos);
const handleAddNew = () => {
setAddNewTask(addNewTask ? false : true);
};
const handleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
dispatch(createTask(formData));
dispatch(getTasks())
};
return (
<Grow in>
<Container>
<Grid
className={classes.adjustTop}
container
justify="space-between"
alignItems="stretch"
spacing={3}
>
<Grid item xs={2} sm={2}></Grid>
<Grid item xs={8} sm={8}>
<Typography variant="h2">TODO LIST</Typography>
{todos==null?
<Typography>Loading...</Typography>
:
<>
{todos?.tasks?.map((todo) => (
<Task id={todo.task} todo={todo} />
))}
</>
}
<div style={{ textAlign: "center" }}>
{addNewTask ? (
<>
<Button
variant="contained"
size="large"
color="error"
style={{ marginTop: "10px" }}
onClick={handleAddNew}
>
Cancel Adding task
</Button>
<form style={{ paddingTop: "20px" }} onSubmit={handleSubmit}>
<Input
name="taskText"
label="Task Text"
handleChange={handleChange}
type="text"
placeholder="text"
/>
<br />
<Button type="submit">Submit</Button>
</form>
</>
) : (
<Button
variant="contained"
size="large"
color="primary"
style={{ marginTop: "10px" }}
onClick={handleAddNew}
>
+ Add new task
</Button>
)}
</div>
</Grid>
<Grid item xs={2} sm={2}></Grid>
</Grid>
</Container>
</Grow>
);
};
export default Home;
This is the reducer
import { FETCH_ALL,CREATE } from '../constants/actionTypes';
export default (state = {tasks:null}, action) => {
switch (action.type) {
case FETCH_ALL:
return {...state,tasks:action.payload,errors:null}
case CREATE:
return {...state,tasks:action.payload,errors:null}
default:
return state;
}
};
& actions
import { FETCH_ALL,CREATE} from '../constants/actionTypes';
import * as api from '../api/index.js';
export const getTasks = () => async (dispatch) => {
try {
const { data } = await api.fetchTasks();
console.log(data);
dispatch({ type: FETCH_ALL, payload: data });
} catch (error) {
console.log(error.message);
}
};
export const createTask = (taskText) => async (dispatch) => {
try {
const { data } = await api.createTask(taskText);
dispatch({type:CREATE,payload:data})
} catch (error) {
console.log(error.message);
}
};
I am able to add the data to database on submit using handleSubmit tot the database but the issue is after each submit its giving me an error TypeError: _todos$tasks.map is not a function
I tried to handle this by using ternary operator & rendering Loading text on null & also have used chaining operator,but still getting same error
You are trying to get todos from your redux store
const todos = useSelector((state) => state.todos);
But there is no todos in your state and plus you are updating another state tasks in your reducer:
export default (state = {tasks:null}, action) => {
switch (action.type) {
case FETCH_ALL:
return {...state,tasks:action.payload,errors:null}
case CREATE:
return {...state,tasks:action.payload,errors:null}
default:
return state;
}
};
I am trying to figure out why any event triggers a visible page refresh when re-rendering. The following is the parent component :
export default function FullScreenDialog({purchaseList={"testtest":"900","heyzi":"90"}}) {
const [open, setOpen] = React.useState(false);
const [pieChartData,setPieChartData]=React.useState([])
const [formset,setFormset]=React.useState([
{
id: uuidv4(),
product:"",
price: 0,
quantity: 0,
productSubtotal: 0,
}
])
const singleForm= {
id: uuidv4(),
product:"",
price: 0,
quantity: 0,
productSubtotal: 0,
}
const handleProduct = (e,id) => {
const newFormset=formset.map(item=>
(item.id !== id? item : {
...item , product: e.target.value , price:purchaseList[e.target.value]
})
)
setFormset(newFormset)
}
const handleQuantity = (e,id) => {
const newFormset=formset.map((item)=>
{
if (item.id===id){
const newItem={
...item, quantity: e.target.value
}
return newItem
}
return item
})
setFormset(newFormset)
}
const handleAdd=()=>{
setFormset([...formset,singleForm])
}
const handleDelete=(id)=>{
const newFormset=formset.filter(item=>
item.id !==id
)
setFormset(newFormset)
}
//below is solely from this component, up is the state from children
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
handlePieChartData()
};
const handlePieChartData=()=>{
setPieChartData(
[...pieChartData,{id:uuidv4(), data:formset }])
}
console.log(formset)
console.log(pieChartData)
return (
<div>
<ListOfPieChartsPresenter
open={open}
handleClickOpen={handleClickOpen}
handleClose={handleClose}
handleProduct={handleProduct}
handleQuantity={handleQuantity}
handleDelete={handleDelete}
handleAdd={handleAdd}
purchaseList={purchaseList}
formset={formset}/>
</div>
);
}
Here is the ListOfPieChartsPresenter component:
const ListOfPieChartsPresenter=({handleClickOpen,open,handleClose,handleProduct,handleQuantity,handleDelete,handleAdd,purchaseList,formset})=>{
const classes = useStyles();
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open full-screen dialog
</Button>
<Dialog fullScreen open={open} onClose={handleClose} TransitionComponent={Transition}>
<AppBar className={classes.appBar}>
<Toolbar>
<IconButton edge="start" color="inherit" onClick={handleClose} aria-label="close">
{/* <CloseIcon /> */}
</IconButton>
<Typography variant="h6" className={classes.title}>
Sound
</Typography>
<Button autoFocus color="inherit" onClick={handleClose}>
save
</Button>
</Toolbar>
</AppBar>
<form >
<PieChartGroupForm
handleProduct={handleProduct}
handleQuantity={handleQuantity}
handleDelete={handleDelete}
handleAdd={handleAdd}
purchaseList={purchaseList}
formset={formset} />
</form>
</Dialog>
</div>
)
}
Lastly, this is the PieChartGroupFormPresenter:
const PieChartGroupFormPresenter=({handleProduct,handleQuantity,handleDelete,product,quantity,item,purchaseList,id})=>{
return (
<div>
<FormControl>
<Select onChange={(e)=>handleProduct(e,id)} value={product}>
{Object.keys(purchaseList).map((item,index) =>
<MenuItem value={item} key={index}>{item}</MenuItem>
)}
</Select>
<TextField onChange={(e)=>handleQuantity(e,id)} value={quantity} />
<TextField value={item.price} disabled>{item.price}</TextField>
<button onClick={()=>handleDelete(id)}>Delete</button>
</FormControl>
<br>
</br>
</div>
)
}
I have been reviewing this for approx an hour now and I still have no idea why this is happening. Could you please share your opinion? Thank you !
The first issue is that, with function components, every time the parent rerenders, the child will also rerender. You can fix this via React.memo:
const PieChartGroupFormPresenter = React.memo(({handleProduct,handleQuantity,handleDelete,product,quantity,item,purchaseList,id})=>{
//...
});
The second issue is that every time you execute code like const handleAdd=()=>{...} you get a different function (closure). So each time FullScreenDialog's state changes, it's going to change all the props passed to ListOfPieChartsPresenter.
You can use the useCallback hook to improve this: then the function/closure will change only when the specified dependencies change. For example:
const handleAdd = React.useCallback(()=>{
setFormset([...formset,singleForm])
}, [formset, singleForm]);
However, note that this callback function will still change whenever formset or singleForm change, so it will still cause a rerender when this state changes.
So hopefully it's at least clear why rerenders are happening, and why they are difficult to prevent. Unfortunately, I'm not familiar enough with React transitions to figure out why they're triggering for each rerender.
I'm receiving the error 'expected an assignment of function call and instead saw an expression.
*The above error is resolved, it's now giving me an Unhandled Rejection (TypeError): render is not a function. It functions properly until I go to the products page and then gives me the error.
I console logged it and it was pulling the information but then it breaks when I import the ProductBrowse component to display the information.
ProductPage:
class ProductPage extends React.Component {
state = {
shoppingCartOpen: false,
};
drawerToggleClickHandler = () => {
this.setState((prevState) => {
return { shoppingCartOpen: !prevState.shoppingCartOpen };
});
};
render() {
let shoppingCartDrawer;
if (this.state.shoppingCartOpen) {
shoppingCartDrawer = <ShoppingCartDrawer />;
}
return (
<ProductStyling>
<ButtonAppBar drawerClickHandler={this.drawerToggleClickHandler} />
<H1>Products</H1>
{shoppingCartDrawer}
<ProductConsumer>
<Grid container spacing={3}>
{(value) => {
return value.products.map((prod, idx) => {
return (
<Grid item xs={3} key={`${prod.name}-${prod.store}-${idx}`}>
<ProductBrowse
productName={prod.name}
productDesc={prod.desc}
productImg={prod.img}
productPrice={prod.price}
/>
</Grid>
);
});
}}
</Grid>
</ProductConsumer>
;
</ProductStyling>
);
}
}
Structure of value:
const ProductContext = React.createContext();
class ProductProvider extends React.Component {
state = {
products: productData,
};
addToCart = () => {
console.log('Hello from add to cart'); };
render() {
return (
<ProductContext.Provider
value={{ ...this.state, addToCart: this.addToCart }}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
const ProductConsumer = ProductContext.Consumer;
export { ProductProvider, ProductConsumer };
ProductBrowse:
const ProductBrowse = ({
productName,
productDesc,
productImg,
productPrice,
}) => {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const openModal = () => {
setOpen(!open);
};
const closeModal = () => {
setOpen(!open);
};
return (
<Box border={1} borderRadius={3}>
<Card className={classes.root}>
<CardActionArea onClick={() => openModal()}>
<ProductModal
open={open}
onClick={() => openModal()}
onClose={() => closeModal()}
onSave={() => closeModal()}
productName={productName}
productDesc={productDesc}
/>
<CardHeader
title={productName}
subheader={formatCurrency(productPrice)}
/>
<CardMedia
className={classes.media}
image={productImg}
alt={productDesc}
/>
<CardContent>
<Typography variant='body2' color='textSecondary' component='p'>
{productDesc}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button size='small' /*To Checkout*/>BUY NOW</Button>
<Button size='small'>ADD TO CART</Button>
<Button size='small'>REVIEW</Button>
</CardActions>
</Card>
</Box>
);
};
ProductData:
import desk from '../../../assets/img/desk.webp';
export const productData = [
{
img: desk,
name: 'Desk',
store: 'Local Furniture Shop 1',
price: 9.99,
desc: "This sturdy desk is built to outlast years of coffee and hard work. You get a generous work surface and a clever solution to keep cords in place underneath."
},
Index.js:
ReactDOM.render(
<React.StrictMode>
<Auth0Provider
domain={auth0Domain}
client_id={auth0ClientID}
redirect_uri={window.location.origin}
onRedirectCallback={onRedirectCallback}
audience={auth0Audience}
scope={"read:current_user"}
>
<ProductProvider>
<Provider store={store}>
<App />
</Provider>
</ProductProvider>
</Auth0Provider>
</React.StrictMode>,
document.getElementById('root')
);
You are not returning anything from ProductConsumer You need to do like this:
<ProductConsumer>
<Grid container spacing={3}>
{(value) => {
return value.products.map((prod, idx) => {
return (
<Grid item xs={3} key={`${prod.name}-${prod.store}-${idx}`}>
<ProductBrowse
productName={prod.name}
productDesc={prod.desc}
productImg={prod.img}
productPrice={prod.price}
/>
</Grid>
);
});
}}
</Grid>
</ProductConsumer>;
So I've looked everywhere for a solution, with no luck.
I am trying to pass a parent React component's this.state.nextID to the child component as a property. However, when I try to access that property in the child, it is null. I am using Material-UI for react and I think the problem is with the withStyles function, because when I inspect the source of the page, I see the key property on the withStyles(ServerBlock) node. But then there is a child of that node that is ServerBlock, with no key property. What am I doing wrong?
ConfigBlock.js
class ConfigBlock extends Component {
constructor () {
super()
this.state = {
children: [],
nextID: 0
}
this.handleChildUnmount = this.handleChildUnmount.bind(this);
}
handleChildUnmount = (key) => {
console.log(key)
this.state.children.splice(key, 1);
this.setState({children: this.state.children});
}
addServerBlock() {
this.state.children.push({"id": this.state.nextID, "obj": <ServerBlock unmountMe={this.handleChildUnmount} key={this.state.nextID} />})
this.setState({children: this.state.children})
this.state.nextID += 1
}
addUpstreamBlock() {
this.state.children.push({"id": this.state.nextID, "obj": <UpstreamBlock unmountMe={this.handleChildUnmount} key={this.state.nextID} />})
this.setState({children: this.state.children})
this.state.nextID += 1
}
render () {
const {classes} = this.props;
return (
<div className={classes.container}>
<Card className={classes.card}>
<CardContent>
<Typography className={classes.title} color="primary">
Config
</Typography>
<div>
{this.state.children.map((child, index) => {
return (child.obj);
})}
</div>
</CardContent>
<CardActions>
<Button variant="contained" color="primary" className={classes.button} onClick={ this.addServerBlock.bind(this) }>
Server
<AddIcon />
</Button>
<Button variant="contained" color="primary" className={classes.button} onClick={ this.addUpstreamBlock.bind(this) }>
Upstream
<AddIcon />
</Button>
</CardActions>
</Card>
</div>
);
}
}
ConfigBlock.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(ConfigBlock);
ServerBlock.js
class ServerBlock extends Component {
constructor (props) {
super(props)
this.state = {
children: []
}
}
addServerBlock() {
this.state.children.push(<NginxEntry/>)
this.setState({children: this.state.children})
}
deleteMe = () => {
this.props.unmountMe(this.props.key);
}
render () {
const {classes} = this.props;
return (
<div className={classes.container}>
<Card className={classes.card}>
<CardContent>
<Typography className={classes.title} color="primary">
Server
</Typography>
</CardContent>
<CardActions>
<Button variant="contained" color="primary" className={classes.button} onClick={() => { console.log('onClick'); }}>
Key/Value
<AddIcon />
</Button>
<Button variant="contained" color="primary" className={classes.button} onClick={() => { console.log('onClick'); }}>
Location
<AddIcon />
</Button>
<Button variant="contained" color="primary" className={classes.button} onClick={() => { console.log('onClick'); }}>
Comment
<AddIcon />
</Button>
<Button variant="contained" color="primary" className={classes.button} onClick={ this.deleteMe }>
<DeleteIcon />
</Button>
</CardActions>
</Card>
</div>
);
}
}
ServerBlock.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(ServerBlock);
key is a special React attribute, it is not a prop, i.e. the child will never have access to it's value. If the child needs to use that value, provide it via another prop (as well as via the key), e.g.
<ServerBlock
unmountMe={this.handleChildUnmount}
key={this.state.nextID}
id={this.state.nextID}
/>
...Aside from that, your code is quite unusual. You should not be directly mutating state (you should always use setState), and you wouldn't normally store whole components in your state. Just as food for thought here's an alternative (untested) implementation of your ConfigBlock component that uses setState and shifts around some of the logic a bit:
class ConfigBlock extends Component {
constructor () {
super()
this.state = {
nextID = 0,
children: [],
}
this.handleChildUnmount = this.handleChildUnmount.bind(this);
// bind this function once here rather than creating new bound functions every render
this.addBlock = this.addBlock.bind(this)
}
handleChildUnmount = (key) => {
console.log(key)
this.setState(state => {
return {
// `state.children.splice(key, 1)`, aside from mutating the state,
// will not work as expected after the first unmount as the ids and
// array positions won't stay aligned
children: state.children.slice().filter(child => child.id !== key)
}
})
}
// Consolidate the two addBlock functions, given we're determining the type
// of component to render in the render function.
addBlock(blockType) {
this.setState(state => {
return {
children: [...state.children, { id: state.nextID, type: blockType }]
nextID: state.nextID + 1
}
})
}
render () {
const {classes} = this.props;
return (
<div className={classes.container}>
<Card className={classes.card}>
<CardContent>
<Typography className={classes.title} color="primary">
Config
</Typography>
<div>
{this.state.children.map(child => {
// determine the component to render here rather than in the handlers
if (child.type === 'server') {
return <ServerBlock key={child.id} id={child.id} unmountMe={this.handleChildUnmount(child.id)} />
} else if (child.type === 'upstream') {
return <UpstreamBlock key={child.id} id={child.id} unmountMe={this.handleChildUnmount(child.id)} />
}
})}
</div>
</CardContent>
<CardActions>
<Button variant="contained" color="primary" className={classes.button} onClick={this.addBlock('server')}>
Server
<AddIcon />
</Button>
<Button variant="contained" color="primary" className={classes.button} onClick={this.addBlock('upstream')}>
Upstream
<AddIcon />
</Button>
</CardActions>
</Card>
</div>
);
}
}
I am very new at React and Redux both, I have only been using both for about 2 - 3 weeks to develop an alpha version of an application.
Though most of the tutorials on using Redux with React seemed to be pretty complicated I found one that allowed me to get some quick code down to try very simple scenarios within my application.
The main problem I seem to face at the moment is: I want to click on an image of and show the details of said property on another page (routed to using react-router, passing id in the path - to clarify in the current code i am using the hardcoded id of 22 an the id is not passed in the path yet). I thought it would as straight forward as clicking on the app then in either the constructor or componentWillMount method I could call this.props.foo(id) and then get the property using this.props.store.foo but it seems as though the store is not updated at that time. But if i called this.props.foo(id) in the handleClick method of the page before redirecting then it would work, but on refresh the store is back to default and causes an error.
I am just wondering if I am just taking a completely wrong approach to this.. or just missing something.
The code may be too much, just let me know if I should trim it down...
Functions to look for are the:
handleImageClick() - > Results.js
constructor() - > BuyDetails.js
Code:
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;
Thanks Everyone... In Advance :)
+++UPDATE - Still not working+++
Here is the code for another approach i tried which was just calling the dispatch in componentWillMount() then just passing this.props.store.selectedState directly to the child component.
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;
I wouldn't fetch the item in the details component, at least not explicitly.
Consider:
A details component:
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);
A list component:
class ListComponent extends React.Component {
...
onImageClick = (item) => {
this.props.setSelectedItem(item);
}
...
}
This relies on set/getSelectedItem actions which set some relevant state. The details component will automatically grab the selected item when it's mounted.
Another thing to consider would be, if the two components were being rendered simultaneously (in a list/detail style UI, for example), would be to lift the selected state up into the parent state (parent of both components).
class ParentComponent extends React.Component {
...
onItemSelected = (item) => {
this.setState({ selectedItem: item });
}
render() {
return (
<ListComponent onItemSelected={ this.onItemSelected }/>
<DetailsComponent item={ this.state.selectedItem }/>
);
}
}
That all said, you posted a lot of code and it's a little hard to tell what's going on. Hopefully something I've written above helps with your issue.