I have the problem that i cannot find a way to get the mapDispatchToProps into my react component. since it is a class.
This is the class where i have the problem, i get the error: 'findPosts' is not defined.
const mapDispatchToProps = {
findPosts: searchPosts
};
class SearchMenu extends React.Component {
state = {
anchorEl: null,
value: ''
};
handleChange = event => {
this.setState({value: event.target.value,});
};
handleClick = event => {
this.setState({ anchorEl: event.currentTarget });
};
handleClose = () => {
this.setState({ anchorEl: null });
};
render() {
const { anchorEl } = this.state;
return (
<Fragment>
<IconButton onClick={this.handleClick}>
<SearchIcon color="secondary" />
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={this.handleClose}
>
<MenuItem>
<CategorySelector />
</MenuItem>
<MenuItem>
<TextField
value={this.state.value}
onChange={this.handleChange}
/>
<IconButton onClick={() => { findPosts(this.state.value); }}>
<SearchIcon color="secondary" />
</IconButton>
</MenuItem>
</Menu>
</Fragment>
);
}
}
export default connect(null,mapDispatchToProps)(SearchMenu);
In other components I was able to do this because I could use something like this:
const mapDispatchToProps = {
getPosts: fetchPosts
};
const Reactions = ({ getPosts}) => (
<LinkButton onClick={() => {getPosts("messages");}}>
Back to posts
</LinkButton>
);
export default connect(null, mapDispatchToProps)(Reactions);
But because I need the this.setState in the bit of code, I am not able to change it like in the second example.
In class you have to use this.props.findPosts(this.state.value)
Related
Material-ui component is not rendering in react class based component ?
I am trying to convert function based material-ui component in class based component but the class based component is not rendering... in main div in inspect main div not showing that component?
any idea how to use material-ui with class based component?
enter code here
import * as React from 'react';
import Box from '#mui/material/Box';
import Tabs from '#mui/material/Tabs';
import Tab from '#mui/material/Tab';
function LinkTab(props) {
return (
<Tab
component="a"
onClick={(event) => {
event.preventDefault();
}}
{...props}
/>
);
}
class Header extends Component {
constructor(props) {
super(props);
this.state = {
value: 0,
};
}
handleChange = (event, newValue) => {
this.setState(
(prevState) => (
{
value: newValue,
},
() => {
console.log("value", this.state.newValue);
}
)
);
};
render() {
return (
<Box sx={{ width: '100%' }}>
<Tabs value={this.state.value} onChange={this.handleChange()} aria-label="nav tabs example">
<LinkTab label="Page One" href="/drafts" />
<LinkTab label="Page Two" href="/trash" />
<LinkTab label="Page Three" href="/spam" />
</Tabs>
</Box>
);
}
}
export default Header;
import * as React from 'react';
import Box from '#mui/material/Box';
import Tabs from '#mui/material/Tabs';
import Tab from '#mui/material/Tab';
function LinkTab(props) {
return (
<Tab
component="a"
onClick={(event) => {
event.preventDefault();
}}
{...props}
/>
);
}
export default function header() {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Box sx={{ width: '100%' }}>
<Tabs value={value} onChange={handleChange} aria-label="nav tabs example">
<LinkTab label="Page One" href="/drafts" />
<LinkTab label="Page Two" href="/trash" />
<LinkTab label="Page Three" href="/spam" />
</Tabs>
</Box>
);
}
Replace
<Tabs value={this.state.value} onChange={this.handleChange()}
by
<Tabs value={this.state.value} onChange={this.handleChange}
Replace
handleChange = (event, newValue) => {
this.setState(
(prevState) => (
{
value: newValue,
},
() => {
console.log("value", this.state.newValue);
}
)
);
};
by
handleChange = (event, newValue) => {
this.setState(
{value: newValue},
() => {
console.log("value", this.state.newValue);
}
);
};
I can't give 100% width on the snackbar and I also have a snackbarClose method but I can't implement it on the snackbar. I also want a button 'X' which button will perform the snackbarClose method.
CodeSandbox : https://codesandbox.io/s/xenodochial-kapitsa-f5yd7?file=/src/Demo.js:693-706
import React, { Component } from "react";
import { Container, Grid, Button, Snackbar } from "#material-ui/core";
import MuiAlert from "#material-ui/lab/Alert";
import { withStyles } from "#material-ui/core/styles";
const styles = (theme) => ({});
function Alert(props) {
return <MuiAlert elevation={6} variant="filled" {...props} />;
}
class Demo extends Component {
constructor() {
super();
this.state = {
snackbaropen: false,
snackbarmsg: "",
severity: ""
};
this.onClick = this.onClick.bind(this);
}
onClick = (event) => {
this.setState({
snackbaropen: true,
snackbarmsg: "Data Saved",
severity: "success"
});
};
snackbarClose = (event) => {
this.setState({ snackbaropen: false });
};
render() {
const { classes } = this.props;
return (
<React.Fragment>
<Container>
<Grid container spacing={2}>
<Grid item xs={12} sm={6} md={6} lg={6} xl={6}>
<Button
variant="contained"
color="primary"
onClick={this.onClick}
>
Submit
</Button>
<Snackbar
open={this.state.snackbaropen}
autoHideDuration={3000}
onClose={this.snackbarClose}
>
<Alert severity={this.state.severity}>
{this.state.snackbarmsg}
</Alert>
</Snackbar>
</Grid>
</Grid>
</Container>
</React.Fragment>
);
}
}
export default withStyles(styles, { withTheme: true })(Demo);
If you want 100% width on snackbar, you need to specify width for Alert and Snackbar component and for close button you need to specify onClose function on Alert component.
<Snackbar
open={this.state.snackbaropen}
autoHideDuration={3000}
onClose={this.snackbarClose}
style={{ width: "100%" }} // specify width 100%
>
<Alert
onClose={this.snackbarClose} // specify onClose method
severity={this.state.severity}
style={{ width: "100%" }} // specify width 100%
>
{this.state.snackbarmsg}
</Alert>
</Snackbar>
For snackback closing on outside click, you need to change close function like below:-
snackbarClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
this.setState({ snackbaropen: false });
};
Demo: https://codesandbox.io/s/heuristic-khayyam-xo464
This is so complicated to solve problem.
What React version used?
If you used React(over 16.8), I recommend solution I used.
Use React.createContext, React.useContext
Use React.useReducer
Make custom Hook like useSnackBar
Mount SnackBarProvider on the App
import React from 'react';
import ToastContext, { ToastProps } from './ToastContext';
const useToast = () => {
const [, dispatch] = React.useContext(ToastContext);
const message = (toast: ToastProps) => {
dispatch({
type: 'ADD_TOAST',
payload: toast,
});
};
return message;
};
export { useToast };
export default useToast;
const ToastProvider: React.FC<ToastProviderProps> = ({
children,
placement = 'top-right',
timeout = 5000,
}) => {
const [toasts, dispatch] = React.useReducer(toastReducer, []);
return (
<ToastContext.Provider value={[toasts, dispatch]}>
<ToastProviderWrapper>
<ToastProviderContainer className={classnames(placement)}>
{toasts.map((toast, index) => (
<ToastCard {...toast} key={index} timeout={timeout} />
))}
</ToastProviderContainer>
</ToastProviderWrapper>
{children}
</ToastContext.Provider>
);
};
const Index = () => (
<ToastProvider>
<App />
</ToastProvider>
);
const rootElement = document.getElementById('root');
ReactDOM.render(<Index />, rootElement);
You can give data through dispatch(action), reducers
You can Dispatch event all the pages.
These Doms were rendered Root Element like React.Portal. then you can edit global position styles as system.
The SnackBar component delegates to or inherits its style or appearance from its children component, so you can instead adjust the width of the Alert component inside the SnackBar.
<Alert style={{ width: "100%" }} severity={this.state.severity}>
{this.state.snackbarmsg}
</Alert>
This should also adjust the width of the SnackBar component and make it fullwidth.
I'm trying to create a header component that changes based on which screen you're viewing. However, the child component is not working if I try to create some dynamic buttons.
I have split each part of the header into smaller components, the problem is that the right side of the header HeaderRightSide is not rendering unless I add each button myself.
const screenIcon = {
Home: 'home',
Settings: 'tools'
}
export class AppHeader extends Component {
static propTypes = {
screenName: PropTypes.string.isRequired,
navigation: PropTypes.object.isRequired,
googleUser: PropTypes.object.isRequired,
UserSignIn_logOut: PropTypes.func.isRequired
}
signOut = async () => {
try {
await GoogleSignin.revokeAccess();
await GoogleSignin.signOut();
} catch (error) {
// console.error(error);
} finally {
this.props.UserSignIn_logOut({});
this.props.navigation.navigate('SignIn');
}
}
componentDidMount() {
console.log(this.props.navigation.state)
}
render() {
return (
<Header>
<HeaderLeftSide screenName={this.props.screenName} />
<HeaderBody screenName={this.props.screenName} />
<HeaderRightSide screenName={this.props.screenName} navigation={this.props.navigation} />
</Header>
)
}
}
HeaderLeftSide = (props) => (
<Left>
<Button transparent>
<Icon name={screenIcon[props.screenName]} />
</Button>
</Left>
)
HeaderBody = (props) => (
<Body>
<Title>{props.screenName}</Title>
</Body>
)
HeaderRightSide = (props) => (
<Right>
{Object.keys(screenIcon).forEach(screen =>
( <Button transparent>
<Icon name={screenIcon[screen]} />
</Button>)
)}
</Right>
)
const mapStateToProps = (state) => ({
googleUser: state.UserSignInReducer.googleUser
})
const mapDispatchToProps = {
UserSignIn_logOut: () => {
dispatch(UserSignIn_logOut());
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AppHeader)
I just had to change forEach to map. Correct code:
HeaderRightSide = (props) => (
<Right>
{Object.keys(screenIcon).map(screen => (
<Button transparent>
<Icon name={screenIcon[screen]} />
</Button>
)
)}
</Right>
)
The following code renders a like object when its clicked.
So when console.log(nextProps.myPosts) exectues
it goes from this
Likes(95)
to this
Likes(96)
Currently im getting the like counts like
myLikes={post.Likes.length}
And its not showing the reflected change on the ui, so what should i change so it can reflect the updated prop ?
Posts.js
class Posts extends Component {
state = {
posts: [],
loading: true,
isEditing: false,
// likes:[]
}
componentWillMount(){
this.props.GetPosts();
this.setState({
loading:false
})
}
componentWillReceiveProps(nextProps, prevState) {
let hasNewLike = true ;
if(prevState.posts && prevState.posts.length) {
for(let index=0; index < nextProps.myPosts.length; index++) {
if(nextProps.myPosts[index].Likes.length !=
prevState.posts[index].Likes.length) {
hasNewLike = false;
}
}
this.setState({posts: nextProps.myPosts}); // here we are updating the posts state if redux state has updated value of likes
console.log(nextProps.myPosts)
}
render() {
const {loading} = this.state;
const { myPosts} = this.props
if (!this.props.isAuthenticated) {
return (<Redirect to='/signIn' />);
}
if(loading){
return "loading..."
}
return (
<div className="App" style={Styles.wrapper}>
<h1> Posts </h1>
<PostList posts={this.state.posts}/>
</div>
);
}
}
const mapStateToProps = (state) => ({
isAuthenticated: state.user.isAuthenticated,
myPosts: state.post.posts,
})
const mapDispatchToProps = (dispatch, state) => ({
GetPosts: () => dispatch( GetPosts())
});
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(Posts));
PostList.js
class PostList extends Component{
constructor(props){
super(props);
this.state ={
title: '',
}
}
// Return a new function. Otherwise the DeletePost action will be dispatch each
// time the Component rerenders.
removePost = (id) => () => {
this.props.DeletePost(id);
}
onChange = (e) => {
e.preventDefault();
this.setState({
title: e.target.value
})
}
formEditing = (id) => ()=> {;
this.props.EditChange(id);
}
render(){
const {posts} = this.props;
return (
<div>
{posts.map(post => (
<Paper key={post.id} style={Styles.myPaper}>
<PostItem
myLikes={post.Likes.length} // right here
myTitle={this.state.title}
editChange={this.onChange}
editForm={this.formEditing}
isEditing={this.props.isEditingId === post.id}
removePost={this.removePost}
{...post}
/>
</Paper>
))}
</div>
);
}
}
const mapStateToProps = (state) => ({
isEditingId: state.post.isEditingId,
// ourLikes: state.post.likes // reducer likes
})
const mapDispatchToProps = (dispatch) => ({
// pass creds which can be called anything, but i just call it credentials but it should be called something more
// specific.
EditChange: (id) => dispatch(EditChange(id)),
UpdatePost: (creds) => dispatch(UpdatePost(creds)),
postLike: (id) => dispatch( postLike(id)),
// Pass id to the DeletePost functions.
DeletePost: (id) => dispatch(DeletePost(id))
});
export default connect(mapStateToProps, mapDispatchToProps)(PostList);
reducer
const initialState = {
post: [],
postError: null,
posts:[],
isEditing:false,
isEditingId:null,
likes:[],
someLike:[],
postId:null
}
export default (state = initialState, action) => {
switch (action.type) {
case GET_POSTS:
console.log(action.data)
return {
...state,
posts: action.data, // maps posts fine,
}
case ADD_LIKE:
const newState = {...state}; // here I am trying to shallow copy the existing state;
const existingLikesOfPost = newState.posts.find(post => post.id == action.id).Likes;
newState.posts.find(post => post.id == action.id).Likes = [...existingLikesOfPost, action.newLikeObject]; // using this approach I got some code duplication so I suggested the first approach of using **push** method of array.
return newState
Like component
import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faCoffee, faAdjust } from '#fortawesome/free-solid-svg-icons';
import {connect} from 'react-redux';
import { postLike} from '../actions/';
class Like extends Component{
constructor(props){
super(props);
this.state = {
likes: null,
heart: false
}
}
// passes post id thats stored in PostItem.js
clickLike = (id) => {
this.props.postLike(id);
// toggles between css class
this.setState({
heart: !this.state.heart
})
}
render(){
return(
<div style={{float:'right', fontSize: '1.5em', color:'tomato'}} >
<i style={{ marginRight: '140px'}} className={this.state.heart ? 'fa fa-heart':'fa fa-heart-o' }>
<span style={{ marginLeft: '6px'}}>
<a href="#" onClick={() =>this.clickLike(this.props.like)}>Like</a>
</span>
{/* gets the like counts */}
<span style={{ marginLeft: '7px'}} >{this.props.likes} </span>
</i>
</div>
)
}
}
const mapStateToProps = (state) => ({
isEditingId: state.post.isEditingId,
// myLikes: state.post.likes
})
const mapDispatchToProps = (dispatch) => ({
postLike: (id) => dispatch( postLike(id))
// Pass id to the DeletePost functions.
});
export default connect(mapStateToProps, mapDispatchToProps)(Like);
PostItem.js
.....
class PostItem extends Component{
constructor(props){
super(props);
this.state = {
disabled: false,
}
}
onUpdate = (id, title) => () => {
// we need the id so expres knows what post to update, and the title being that only editing the title.
if(this.props.myTitle !== null){
const creds = {
id, title
}
this.props.UpdatePost(creds);
}
}
render(){
const {title, id, userId, removePost, createdAt, post_content, username, editForm, isEditing, editChange, myTitle, postUpdate, Likes, clickLike, myLikes} = this.props
return(
<div>
<Typography variant="h6" component="h3">
{/* if else teneray operator */}
{isEditing ? (
<Editable editField={myTitle ? myTitle : title} editChange={editChange}/>
): (
<div>
{title}
</div>
)}
</Typography>
<Typography component={'span'} variant={'body2'}>
{post_content}
<h5>by: {username} </h5>
{/* component span cancels out the cant be a decedent of error */}
<Typography component={'span'} variant={'body2'} color="textSecondary">{moment(createdAt).calendar()}</Typography>
{/* gets like counts */}
<Like like={id} likes={myLikes} />
</Typography>
{!isEditing ? (
<Button variant="outlined" type="submit" onClick={editForm(id)}>
Edit
</Button>
):(
// pass id, and myTitle which as we remember myTitle is the new value when updating the title
<div>
<Button
disabled={myTitle.length <= 3}
variant="outlined"
onClick={this.onUpdate(id, myTitle)}>
Update
</Button>
<Button
variant="outlined"
style={{marginLeft: '0.7%'}}
onClick={editForm(null)}>
Close
</Button>
</div>
)}
{!isEditing && (
<Button
style={{marginLeft: '0.7%'}}
variant="outlined"
color="primary"
type="submit"
onClick={removePost(id)}>
Remove
</Button>
)}
</div>
)
}
}
const mapStateToProps = (state) => ({
isEditingId: state.post.isEditingId,
})
const mapDispatchToProps = (dispatch) => ({
// pass creds which can be called anything, but i just call it credentials but it should be called something more
// specific.
UpdatePost: (creds) => dispatch(UpdatePost(creds)),
postLike: (id) => dispatch( postLike(id)),
// Pass id to the DeletePost functions.
});
export default connect(mapStateToProps, mapDispatchToProps)(PostItem);
Following what you wrote, shouldn't hasNewLike be set to true if the comparison of the two lengths are not equal? Meaning that it has a new like? :)
That way we can update the component state and pass that into your child PostList component.
if(nextProps.myPosts[index].Likes.length !=
prevState.posts[index].Likes.length) {
hasNewLike = true;
}
I've copying the instructions on how to use the Menus in Material-UI docs. I do have the button rendered in my container component that has the Paper component but when I click it, the menu is not showing up. Here's the code:
index.js - Container component
const styles = theme => ({
root: theme.mixins.gutters({
paddingBottom: 16
})
});
class ProvidersPage extends React.Component {
constructor(props, context) {
super(props, context);
}
render() {
const { classes, providers } = this.props;
return (
<div>
<Paper className={classes.root} elevation={4}>
<ProviderList providers={providers} />
</Paper>
</div>
);
}
}
ProvidersPage.propTypes = {
classes: PropTypes.object.isRequired,
};
function mapStateToProps(state) {
return {
providers: state.providers
};
}
export default connect(
mapStateToProps
)(withStyles(styles)(ProvidersPage));
ProvidersList.js component
const styles = theme => ({
actions: {
color: theme.palette.text.secondary,
},
title: {
flex: '1',
}
});
class ProviderList extends React.Component {
constructor(props, context) {
super(props, context);
}
render() {
const { classes, providers } = this.props;
return (
<Fragment>
<Toolbar disableGutters>
<div className={classes.title}>
<Typography variant="title">
Providers
</Typography>
</div>
<div className={classes.actions}>
<ProviderMenu />
</div>
</Toolbar>
</Fragment>
);
}
};
ProviderList.propTypes = {
classes: PropTypes.object.isRequired,
providers: PropTypes.array.isRequired
};
export default withStyles(styles)(ProviderList);
ProviderMenu.js - My menu component that has the menus
class ProviderMenu extends React.Component {
state = {
anchorEl: null,
};
handleClick = event => {
this.setState({ anchorEl: event.currentTarget });
};
handleClose = () => {
this.setState({ anchorEl: null });
};
render() {
const { anchorEl } = this.state;
return (
<div>
<Button
aria-owns={anchorEl ? 'simple-menu' : null}
aria-haspopup="true"
onClick={this.handleClick}
>
Open Menu
</Button>
<Menu
id="simple-menu"
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={this.handleClose}
>
<MenuItem onClick={this.handleClose}>Profile</MenuItem>
<MenuItem onClick={this.handleClose}>My account</MenuItem>
<MenuItem onClick={this.handleClose}>Logout</MenuItem>
</Menu>
</div>
);
}
}
export default ProviderMenu;