Spesification
Library
#apollo/client": "^3.0.0"
React.JS
GraphQL query : GET_ALL_USERS
query($input: GetAllUserInput) {
getAllUsers(input: $input) {
totalPages
currentPage
totalItems
rows {
email
username
created_dtm
status
group_id
}
}
}
GraphQL Mutation : DELETE_USER
mutation($username: String!) {
deleteUser(username: $username) {
username
}
}
Problem
1.When I'm click on Delete Button, UI doesn't update but in the backend, item successfully deleted
const TableUserInfo = ({ data }) => {
const [showModal, setShowModal] = useState(false);
const [username, setUsername] = useState("");
const handleShowModal = () => setShowModal(true);
const handleCloseModal = () => setShowModal(false);
const userIdtoDelete = data.getAllUsers.rows.map((row) => row.username);
const userIdToString = userIdtoDelete.toString();
const [deleteUser, { error, loading, refetch }] = useMutation(DELETE_USER, {
variables: { username: userIdToString },
update(cache, { data }) {
const { getAllUsers } = cache.readQuery({ query: GET_ALL_USERS });
cache.writeQuery({
query: GET_ALL_USERS,
data: {
getAllUsers: getAllUsers.filter(
(user) => user.username !== userIdToString
),
},
});
},
onCompleted: () => {
refetch();
},
onError: (error) => {
console.log(JSON.stringify(error, null, 2));
},
});
...
<Button
key="Hapus"
variant="outline-danger"
onClick={deleteUser}
disabled={loading}
className="d-block btn-block btn-sm mb-2 w-100"
>
Hapus
</Button>
Additional Info : Data comes from parent. here is the parent code :
const PengaturanPengguna = () => {
// pagination
const [currentPage, setCurrentPage] = useState(1);
const sizeFetchDataPerPage = 10;
const { data, refetch, loading, error } = useQuery(GET_ALL_USERS, {
variables: {
input: {
size: sizeFetchDataPerPage,
page: currentPage,
},
},
});
const totalPages = data?.getAllUsers.totalPages;
useEffect(() => {
if (data) {
setCurrentPage(data?.getAllUsers.currentPage);
}
}, [data])
if (loading) return <p>Loading...</p>;
if (error)
return (
<p>
Error... {error.message} {console.log(JSON.stringify(error, null, 2))}{" "}
</p>
);
// refetch data
const handleRefreshClick = () => {
refetch();
};
return (
<Container>
<CardInfo data={data} />
<Card>
<Card.Title>
<span className="base-md text-regular mt-2 std-txt-primary-200">
Data Pengguna Dashboard
</span>
</Card.Title>
<Card.Body>
<div className="d-flex justify-content-between">
<Form inline>
<Search />
<Button variant='secondary' onClick={handleRefreshClick} disabled={loading}>{loading ? 'Loading...' : 'Muat Ulang'}</Button>
</Form>
<div>
<Button variant="success">Daftar Akun</Button>
</div>
</div>
<TableUserInfo data={data} /> //heres the table
<div className="d-flex justify-content-between">
<div className="align-self-center">
<span>Keterangan : </span>
<StyledDiv>
<Legend color="#40B4BA" />
<StyledSpan>Aktif</StyledSpan>
</StyledDiv>
<StyledDiv>
<Legend color="#78909C" />
<StyledSpan>Tidak Aktif</StyledSpan>
</StyledDiv>
</div>
<PageNavigator
totalPages={totalPages}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
/>
</div>
</Card.Body>
</Card>
</Container>
);
};
I got this error information
What I've Tried
I'm using refetchQueries, update method with cache.readQuery() and cache.writeQuery but UI still doesn't update.
Question
How to make delete mutation deleted data in the backend and update the UI based on my query and mutation spesification ?
Update
Now, when i'm using deleteUser mutation directly onClick button, i'm getting this errors :
and here's the code :
const [deleteUser, { error, loading, refetch }] = useMutation(DELETE_USER);
<Button
key="Hapus"
variant="outline-danger"
onClick={() => {
const userToDelete = user.username;
deleteUser({
variables: { username: userToDelete },
update(cache, { data }) {
const { getAllUsers } = cache.readQuery({
query: GET_ALL_USERS,
variables,
});
cache.writeQuery({
query: GET_ALL_USERS,
variables,
getAllUsers: {
...getAllUsers,
totalItems: getAllUsers.totalItems - 1,
rows: getAllUsers.rows.filter((user) => user.username !== userToDelete)
}
});
},
onCompleted: () => {
refetch();
},
onError: (error) => {
console.log(JSON.stringify(error, null, 2));
},
});
}}
disabled={loading}
className="d-block btn-block btn-sm mb-2 w-100"
>
Hapus
</Button>
When you read/write a query from the cache you must pass the same variables as the orinigal query, and to do that you can use the variables returned from the original query:
Parent component
const { data, refetch, loading, error, variables } = useQuery(GET_ALL_USERS, {
variables: {
input: {
size: sizeFetchDataPerPage,
page: currentPage,
},
},
});
...
<TableUserInfo data={data} variables={variables} /> //heres the table
...
Child component
const TableUserInfo = ({ data, variables }) => {
...
const { getAllUsers } = cache.readQuery({ query: GET_ALL_USERS, variables });
cache.writeQuery({
query: GET_ALL_USERS,
variables,
data: {
getAllUsers: getAllUsers.filter(
(user) => user.username !== userIdToString
),
},
});
...
Also I think this part of the code is wrong:
const userIdtoDelete = data.getAllUsers.rows.map((row) => row.username);
const userIdToString = userIdtoDelete.toString();
...
data: {
getAllUsers: getAllUsers.filter(
(user) => user.username !== userIdToString
),
},
First you access row property of getAllUsers, you return an array of username then you use .toString() in this array, but when you update the cache you use .filter directly on getAllUsers and you compare the username property with the stringified array of username (userIdToString), so I think you should verify this part for possible mistakes
maybe you are missing the rows here:
getAllUsers: {
...getAllUsers,
totalItems: getAllUsers.totalItems - 1,
rows: getAllUsers.rows.filter((user) => user.username !== userIdToString)
},
Related
export default function SpecificPostCommentsExtended({ article }) {
const [prev, setPrev] = useState("");
const [loaded, setLoaded] = useState(false);
const [comments, setComments] = useState([]);
function changePrevState(_id) {
setPrev(_id);
console.log(_id, "-is id");
console.log(prev, "- prev");
}
const ifNoCom = async () => {
setLoaded(true);
setTimeout(function () {
document
.querySelector("#confirm")
.addEventListener("click", async () => {
const data = await axios({
url: vars.BACKENDURL + "/comment",
withCredentials: true,
method: "POST",
data: {
article: article,
comment: {
content: document.querySelector("#commentcontent").value,
prevId: prev === "" ? null : prev,
},
},
});
setLoaded(true);
});
}, 30);
return;
};
const ifCom = async () => {
let i = 0;
await article.commentsArr.forEach(async (c) => {
const { data } = await axios({
url: vars.BACKENDURL + "/getcomment",
withCredentials: true,
method: "POST",
data: { comment: { _id: c } },
});
if (!comments.includes({ ...data })) {
setComments((current) => [...current, { ...data }]);
}
i++;
if (i === article.commentsArr.length - 1) {
setLoaded(true);
document
.querySelector("#confirm")
.addEventListener("click", async () => {
console.log("It's prev - ", prev, "!lalalal");
const data = await axios({
url: vars.BACKENDURL + "/comment",
withCredentials: true,
method: "POST",
data: {
article: article,
comment: {
content: document.querySelector("#commentcontent").value,
prevId: prev === "" ? null : prev,
},
},
});
});
}
});
};
const getComments = async () => {
setComments([]);
setLoaded(false);
if (article.commentsArr.length === 0) {
ifNoCom();
} else {
ifCom();
}
};
useEffect(() => {
getComments();
}, []);
return (
<>
<Header>
<HeaderImg src="../../assets/headerpic.png" />
<Navbar>
<span>mypage</span>| <span>log out</span>
</Navbar>
</Header>
<Content>
<SideBar />
<RightFrame>
{loaded === false ? (
<CircularProgress />
) : (
<>
<UpperBlock>
<Title>
{article.group.toLowerCase()}
<Subtitle>
<span>previous</span>
<span>next</span>
<span>list</span>
</Subtitle>
</Title>
<PostContainer>
<PostDecription>
<div className="left">
<h2>{article.title}</h2>
<span>{article.writer}</span>
<span>{article.date}</span>
</div>
<div className="right">
<span
onClick={async () => {
window.location = `/${article._id}/edit`;
}}
>
edit
</span>
<span>|</span>
<span
onClick={async () => {
if (
!window.confirm(
"Are you sure you want to delete this post?",
)
) {
return;
}
const { data } = await axios({
url: vars.BACKENDURL + `/deletepost`,
withCredentials: true,
method: "DELETE",
data: {
post: {
id: article._id,
},
},
});
alert(data);
}}
>
delete
</span>
</div>
</PostDecription>
<PostContents>
<h3>Contents</h3>
<p>{article.content}</p>
</PostContents>
</PostContainer>
</UpperBlock>
<LowerBlock>
<ReportBtns>
<ReportBtnMock>inappropriate language</ReportBtnMock>
<ReportBtnMock>misinformation</ReportBtnMock>
</ReportBtns>
<LowerRightFrame>
<div>
<span
onClick={() => {
window.location = "/specificpost/" + article._id;
}}
>
<img src="../../assets/comments.png" /> Comments{" "}
{article.comments}
</span>
<span
onClick={async () => {
const { data } = await axios({
url: vars.BACKENDURL + "/like",
method: "POST",
withCredentials: true,
data: {
post: article,
},
});
alert(data);
}}
>
<img src="../../assets/likes.png" /> Likes{" "}
{article.likes}
</span>
</div>
<div>
<span>Like</span>
<span>|</span>
<span>Report</span>
</div>
</LowerRightFrame>
<CommentsBlock>
{comments.map((c, i) => {
console.log("C comment id", c.comment._id);
const _id = c.comment._id;
return (
<>
<Comment key={i}>
<Nickname>{c.comment.author}</Nickname>
<Contents>{c.comment.content}</Contents>
<LowerCommentContainer>
<span>{c.comment.date}</span>
<span
onClick={(e) => {
changePrevState(_id);
}}
>
reply
</span>
</LowerCommentContainer>
</Comment>
{c.subcomments.map((sc, j) => {
return (
<SubComment key={j}>
<Nickname>{sc.author}</Nickname>
<Contents>
#{sc.author}, <br /> {sc.content}
</Contents>
<LowerCommentContainer>
<span>{sc.date}</span>
</LowerCommentContainer>
</SubComment>
);
})}
</>
);
})}
<ContentsInput id="commentcontent" />
<Confirm id="confirm">Post</Confirm>
</CommentsBlock>
</LowerBlock>
</>
)}
</RightFrame>
</Content>
</>
);
}
Onclick Event's attached to span with label "reply" console logs tell me
60c6f3a623961520f85c23f7 -is id
60c6f3a623961520f85c23f7 - prev
Yet the useEffect's responsible for sending data to Db console logs tell me:
It's prev - !lalalal
So it appears that the prev first gets set and then falls back to initial "" empty string value.
Can you please help me make out how to set the value to that useState hook, what am I doing wrong? Maybe useState variable is overwritten somewhere else and I juts don't know about it? I feel like I'm missing a minor but important!
With an empty array as the second param, your useEffect runs once and once only at the very beginning of the component's lifecycle. At the time of running, the state value is always the initial value "". As a result, the value of prev inside the click handler is always "" since that's essentially a snapshot of the state at the time when useEffect runs.
Instead of document.querySelector("#confirm").addEventListener, add the onClick handler on Confirm directly and access prev inside. This allows you to get the latest of prev value at the time of clicking.
<Confirm id="confirm" onClick={() => console.log('prev value', prev}}>Post</Confirm>
Ref: https://reactjs.org/docs/hooks-faq.html#why-am-i-seeing-stale-props-or-state-inside-my-function
I am working on mern stack e-commerce project . I have a order update route . Order status get updated only by the admin . While updating order status admin gets the default(preload) status . Then admin enter new status for updation .
When I enter (accept) into input fields and hit update button it shows this and status may not get updated .
I am not able to grab orderID .
Here is my update status backend controller
exports.updateStatus = (req, res) => { Order.updateOne(
{ _id: req.body.orderId },
{ $set: { status: req.body.status } },
{ new: true, useFindAndModify: false },
(err, order) => {
if (err) {
return res.status(400).json({ error: "Cannot update order status" });
}
res.json(order);
} );};
update order route
router.put(
"/order-update/:userId",
isSignedIn,
isAuthenticated,
isAdmin,
updateStatus
);
API handler for frontend
export const updateOrder = (userId, token, order) => {
return fetch(`${API}/order-update/${userId}`, {
method: "PUT",
headers: {
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(order),
})
.then((response) => {
return response.json();
})
.catch((err) => console.log(err));
};
Form for updating status
import React, { useEffect, useState } from "react";
import { isAutheticated } from "../auth/helper";
import Base from "../core/Base";
import { getOrder, updateOrder } from "./helper/adminapicall";
const UpdateOrder = ({ match }) => {
const { user, token } = isAutheticated();
const [status, setStatus] = useState("");
const [error, setError] = useState(false);
const [success, setSuccess] = useState(false);
const preload = (orderId) => {
getOrder(orderId, user._id, token).then((data) => {
console.log(data);
if (data?.error) {
setError(data?.error);
} else {
setStatus(data.status);
setError("");
setSuccess("");
}
});
};
useEffect(() => {
preload(match.params.orderId);
}, []);
const handleChange = (event) => {
setError(false);
setStatus(event.target.value);
setSuccess(false);
};
const onSubmit = (e) => {
e.preventDefault();
setError("");
setSuccess("");
updateOrder(user._id, token, { status }).then((data) => {
if (data?.error) {
setError(true);
} else {
setError("");
setSuccess(true);
setStatus("");
}
});
};
const successMessage = () => (
<div
className="alert alert-success mt-3"
style={{ display: success ? "" : "none" }}
>
<h4>updation successfull</h4>
</div>
);
const warningMessage = () => (
<div
className="alert alert-success mt-3"
style={{ display: error ? "" : "none" }}
>
<h4>updation failedl</h4>
</div>
);
const orderUpdateForm = () => (
<form>
<div className="form-group col-md-6">
<p className="lead">Update Order Status</p>
<input
type="text"
className="form-control my-3 col-md-6"
onChange={handleChange}
value={status}
autoFocus
required
/>
<button onClick={onSubmit} className="btn btn-outline-info col-md-6">
Update Order
</button>
</div>
</form>
);
return (
//
<Base title="Update Order Status" description="">
{successMessage()}
{warningMessage()}
{orderUpdateForm()}
</Base>
//
);
};
export default UpdateOrder;
You are trying to fetch orderId from the body but you are not sending OrderId in the body. Also, I am not able to understand why you sen UserId in route "/order-update/:userId".
I think you can send orderId as request param from the front end also you can update your route as
router.put(
"/order-update/:orderId",
isSignedIn,
isAuthenticated,
isAdmin,
updateStatus
);
After that, you can get orderId as
let { orderId } = req.params;
Please help!!
I created a web app using react and connected it with node js.
There I need to pass status of a dish to DishDetail Component whether it is in Favorite or not.
If it is not favorite. I have to mark it as favorite.
Whenever a person click on any dish to make it favorite, an entry is going to be added in favorite collection with user id and dish id.
But whenever a new user logs in and try to add dish as favorite as very first time.I'm facing error that ×TypeError: Cannot read property 'dishes' of undefined at favorite={this.props.favorites.favorites.dishes.some((dish) => dish._id === match.params.dishId)} statement in MainComponent.js and in var favorites = props.favorites.favorites.dishes.map((dish) statement of FavoriteDish.js.
MainComponent.js
const DishWithId = ({match}) => {
if(this.props.favorites.favorites!=null) {
if(Array.isArray(this.props.favorites.favorites)) {
this.props.favorites.favorites=this.props.favorites.favorites[0];
}
}
return(
(this.props.auth.isAuthenticated && !this.props.favorites.isLoading)
?
<DishDetail dish={this.props.dishes.dishes.filter((dish) => dish._id === match.params.dishId)[0]}
isLoading={this.props.dishes.isLoading}
errMess={this.props.dishes.errMess}
comments={this.props.comments.comments.filter((comment) => comment.dish === match.params.dishId)}
commentsErrMess={this.props.comments.errMess}
postComment={this.props.postComment}
favorite={this.props.favorites.favorites.dishes.some((dish) => dish._id === match.params.dishId)}
postFavorite={this.props.postFavorite}
/>
:
<DishDetail dish={this.props.dishes.dishes.filter((dish) => dish._id === match.params.dishId)[0]}
isLoading={this.props.dishes.isLoading}
errMess={this.props.dishes.errMess}
comments={this.props.comments.comments.filter((comment) => comment.dish === match.params.dishId)}
commentsErrMess={this.props.comments.errMess}
postComment={this.props.postComment}
favorite={false}
postFavorite={this.props.postFavorite}
/>
);
}
<Route path="/menu/:dishId" component={DishWithId} />
<PrivateRoute exact path="/favorites" component={() => <Favorites favorites {this.props.favorites} deleteFavorite={this.props.deleteFavorite} />} />
DishDetail.js
const DishDetail = (props) => {
return <RenderDish dish={props.dish} favorite={props.favorite} postFavorite={props.postFavorite} />
}
function RenderDish({dish, favorite, postFavorite}) {
return(
<div className="col-12 col-md-5 m-1">
<FadeTransform in
transformProps={{
exitTransform: 'scale(0.5) translateY(-50%)'
}}>
<Card>
<CardImg top src={baseUrl + dish.image} alt={dish.name} />
<CardImgOverlay>
<Button outline color="primary" onClick={() => favorite ? console.log('Already favorite') : postFavorite(dish._id)}>
{favorite ?
<span className="fa fa-heart"></span>
:
<span className="fa fa-heart-o"></span>
}
</Button>
</CardImgOverlay>
<CardBody>
<CardTitle>{dish.name}</CardTitle>
<CardText>{dish.description}</CardText>
</CardBody>
</Card>
</FadeTransform>
</div>
);
}
FavoriteDish.js
if (props.favorites.favorites) {
if(Array.isArray(props.favorites.favorites))
props.favorites.favorites=props.favorites.favorites[0];
var favorites = props.favorites.favorites.dishes.map((dish) => {
return (
<div key={dish._id} className="col-12 mt-5">
<RenderMenuItem dish={dish} deleteFavorite={props.deleteFavorite} />
</div>
);
});
}
favorite reducer
import * as ActionTypes from './ActionTypes';
export const favorites = (state = {
isLoading: true,
errMess: null,
favorites: null
}, action) => {
switch(action.type) {
case ActionTypes.ADD_FAVORITES:
return {...state, isLoading: false, errMess: null, favorites: action.payload};
case ActionTypes.FAVORITES_LOADING:
return {...state, isLoading: true, errMess: null, favorites: null};
case ActionTypes.FAVORITES_FAILED:
return {...state, isLoading: false, errMess: action.payload, favorites: null};
default:
return state;
}
}
ActionCreator.js
export const fetchFavorites = () => (dispatch) => {
dispatch(favoritesLoading(true));
const bearer = 'Bearer ' + localStorage.getItem('token');
return fetch(baseUrl + 'favorites', {
headers: {
'Authorization': bearer
},
})
.then(response => {
if (response.ok) {
return response;
}
else {
var error = new Error('Error ' + response.status + ': ' + response.statusText);
error.response = response;
throw error;
}
},
error => {
var errmess = new Error(error.message);
throw errmess;
})
.then(response => response.json())
.then(favorites => dispatch(addFavorites(favorites)))
.catch(error => dispatch(favoritesFailed(error.message)));
}
export const favoritesLoading = () => ({
type: ActionTypes.FAVORITES_LOADING
});
export const favoritesFailed = (errmess) => ({
type: ActionTypes.FAVORITES_FAILED,
payload: errmess
});
export const addFavorites = (favorites) => ({
type: ActionTypes.ADD_FAVORITES,
payload: favorites
});
Got idea from Giovanni Esposito.
i just changed this line:- favorite={this.props.favorites.favorites.dishes.some((dish) => dish._id === match.params.dishId)}
to this :- favorite={this.props.favorites.favorites ? this.props.favorites.favorites.dishes.some((dish) => dish._id === match.params.dishId): false}
Ciao, you could modify this line (in DishWithId):
(this.props.auth.isAuthenticated && !this.props.favorites.isLoading)
with:
(this.props.auth.isAuthenticated && !this.props.favorites.isLoading && this.props.favorites.favorites)
and this line (in FavoriteDish.js):
var favorites = props.favorites.favorites.dishes.map((dish)...
with:
if (props.favorites.favorites) {var favorites = props.favorites.favorites.dishes.map((dish)...}
Check condition in favorites such that in case favourite is undefined. It automatically sends false there...
favorite={this.props.favorites.favorites ? this.props.favorites.favorites.dishes.some((dish) => dish._id === match.params.dishId): false}
I have a PostList component with an array of posts objects. I am rendering this list of post using another pure functional component Post using Array.map() method. Post component has another component - LikeButton to like or unlike a post. Now I want to show a spinner during like or unlike on top of that LikeButton component. LikeButton Component looks something like this:
const LikeButton = (props) => {
const likeBtnClasses = [classes.LikeBtn];
const loggedInUserId = useSelector((state) => state.auth.user.id);
const isLoading = useSelector((state) => state.post.loading);
const isPostLiked = props.post.likes.find(
(like) => like.user === loggedInUserId
);
const [isLiked, setLike] = useState(isPostLiked ? true : false);
const token = useSelector((state) => state.auth.token);
const dispatch = useDispatch();
if (isLiked) {
likeBtnClasses.push(classes.Highlight);
}
const postLikeHandler = () => {
if (!isLiked) {
setLike(true);
dispatch(actions.likePost(props.post._id, token));
} else {
setLike(false);
dispatch(actions.unlikePost(props.post._id, token));
}
};
return isLoading ? (
<Spinner />
) : (
<button
className={likeBtnClasses.join(" ")}
onClick={() => postLikeHandler()}
>
<i class="far fa-thumbs-up"></i>
<small>{props.post.likes.length}</small>
</button>
);
};
Instead of showing the spinner to that single post, I am seeing it on all the posts.
My Post component looks like this:
const Post = (props) => {
return (
<div className={classes.Post}>
<div className={classes.Author}>
<img src={props.postData.avatar} alt="avatar" />
<div className={classes.AuthorDetails}>
<h3>{props.postData.name}</h3>
</div>
</div>
<div className={classes.PostText}>
<p>{props.postData.text}</p>
</div>
<hr />
<div className={classes.PostTools}>
<LikeButton post={props.postData} />
<div className={classes.PostBtn}>
<i class="far fa-comments"></i>
<small>3</small>
</div>
<div className={classes.PostBtn}>
<i class="fas fa-share"></i>
<small>2</small>
</div>
</div>
</div>
);
};
PostList component:
class PostList extends React.Component {
state = {
posts: [
{
text: "POST1",
user: "XYZ",
name: "XYZ",
id: "post1",
likes: [],
},
{
text: "POST2",
user: "johndoe#test.com",
name: "John Doe",
id: "post2",
likes: [],
},
],
};
componentDidMount() {
if (this.props.token) {
this.props.onFetchPosts(this.props.token);
this.props.onFetchUserAuthData(this.props.token);
}
}
render() {
let posts = null;
if (this.props.posts.length === 0) {
posts = this.state.posts.map((post) => {
return <Post key={post.id} postData={post} />;
});
} else {
posts = this.props.posts.map((post) => {
return <Post key={post._id} postData={post} />;
});
}
return (
<div>
<CreatePost />
{posts}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
token: state.auth.token,
posts: state.post.posts,
loading: state.post.loading,
error: state.post.err,
};
};
const mapDispatchToProps = (dispatch) => {
return {
onFetchPosts: (token) => dispatch(actions.fetchPosts(token)),
onFetchUserAuthData: (token) => dispatch(actions.fetchUser(token)),
};
};
Please do some change in your to checking like/unlike is loading or not for the LikeButton.
const LikeButton = (props) => {
....
const [isButtonLoading, setButtonLoading] = useState(false);
...
return isButtonLoading ? (
<Spinner />
) : (
<button
className={likeBtnClasses.join(" ")}
onClick={() => postLikeHandler();setButtonLoading(true)}
>
<i class="far fa-thumbs-up"></i>
<small>{props.post.likes.length}</small>
</button>
);
};
Then on your dispatch callback need to set the isButtonLoading value to false.
const buttonCallback() {
// here we need to reset our flag
setButtonLoading(false);
}
const postLikeHandler = () => {
if (!isLiked) {
setLike(true);
// for this action you need to create third parameter called as callback so after response our buttonCallback will call
dispatch(actions.likePost(props.post._id, token, buttonCallback));
} else {
setLike(false);
// for this action you need to create third parameter called as callback so after response our buttonCallback will call
dispatch(actions.unlikePost(props.post._id, token, buttonCallback);
}
};
fore more details please check here.
Hope this will help you.
Im trying to get access a variable called isSuperAdmin, It basically tells me if the logged in user is a super admin or not allowing me to disable some features.
I currently have no access to the variable in the current page however my redux action is showing it as being there, I think I may have configured something incorrectly, as of now my code doesn't change from the initial state value of null to the bool value isSuperUser. Here is the page that I am trying to use this variable.
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import { connect } from 'react-redux';
import Modal from '../Modal';
import Summary from '../Summary';
import s from './BookingDetailsModal.scss';
import AmendConsumerDetails from './AmendConsumerDetails';
import ChangeBookingSession from './ChangeBookingSession';
import payReservationCashActionCreator from '../../actions/payReservationCash';
import payReservationCardActionCreator from '../../actions/payReservationCard';
import payRestActionCreator from '../../actions/payRest';
import refundCashActionCreator from '../../actions/refundCash';
import cancelReservationActionCreator from '../../actions/cancelReservation';
import formatPrice from '../../../../../core/formatPrice';
import {
BOXOFFICE_HIDE_BOOKING_DETAILS,
BOXOFFICE_SET_BOOKING_DETAILS_ACTION_TYPE,
resendConfirmationEmail as resendConfirmationEmailActionCreator,
} from '../../actions';
function renderActionButtons({
isSuperAdmin,
setActionType,
resendConfirmationEmail,
order: {
type: orderType,
paid: orderPaid,
amount: orderAmount,
refundedAt: orderRefundedAt,
canceledAt: orderCanceledAt,
sessionId,
},
isCreatingPayment,
payReservationCard,
payReservationCash,
payRest,
refundCash,
cancelReservation,
}) {
debugger;
return (
<div className={s.buttonsContainer}>
<div className={s.buttonsContainer}>
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
setActionType('AMEND_CONSUMER_DETAILS');
}}
>Amend consumer details</button>
</div>
{ sessionId ?
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
setActionType('CHANGE_SESSION');
}}
>Move to another session</button>
</div> : null
}
<div className={s.buttonContainer}>
<button disabled>Amend tickets or products</button>
</div>
{ orderType === 'reservation' && isCreatingPayment && !orderPaid ?
<div>
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
payReservationCash();
}}
>Pay Reservation CASH</button>
</div>
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
payReservationCard();
}}
>Pay Reservation CARD</button>
</div>
</div> :
null
}
{ orderType === 'deposit' && isCreatingPayment && !orderPaid ?
<div>
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
payRest('CASH');
}}
>Pay Rest CASH</button>
</div>
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
payRest('CARD');
}}
>Pay Rest CARD</button>
</div>
</div> :
null
}
{ !orderRefundedAt && orderPaid ?
<div className={s.buttonContainer}>
<button
disabled={isSuperAdmin}
onClick={(e) => {
e.preventDefault();
refundCash(orderAmount);
}}
>Refund CASH, {formatPrice(orderAmount)}</button>
</div> : null
}
{ orderCanceledAt === null && orderType === 'reservation' ?
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
cancelReservation();
}}
>Cancel Reservation</button>
</div> : null
}
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
resendConfirmationEmail();
}}
>Resend confirmation email</button>
</div>
</div>
</div>
);
}
renderActionButtons.propTypes = {
isSuperAdmin: PropTypes.bool.isRequired,
setActionType: PropTypes.func.isRequired,
resendConfirmationEmail: PropTypes.func.isRequired,
order: PropTypes.shape({
type: PropTypes.string.isRequired,
paid: PropTypes.bool.isRequired,
sessionId: PropTypes.string.isRequired,
amount: PropTypes.number.isRequired,
// reservationPaidCashAt: PropTypes.string.isRequired,
// reservationPaidCardAt: PropTypes.string.isRequired,
}).isRequired,
payReservationCard: PropTypes.func.isRequired,
payReservationCash: PropTypes.func.isRequired,
payRest: PropTypes.func.isRequired,
isCreatingPayment: PropTypes.bool.isRequired,
refundCash: PropTypes.func.isRequired,
cancelReservation: PropTypes.func.isRequired,
};
const components = {
AMEND_CONSUMER_DETAILS: AmendConsumerDetails,
CHANGE_SESSION: ChangeBookingSession,
};
function renderAction(actionType, props) {
const Component = components[actionType];
return <Component {...props} />;
}
function BookingDetailsModal(props) {
const { hideOrderDetails, orderId, bookingDetailsActionType } = props;
return (
<Modal onClose={hideOrderDetails}>
<div className={s.container}>
<div className={s.summaryContainer}>
<Summary orderId={orderId} withEdits={false} />
</div>
<div className={s.actionsContainer}>
{bookingDetailsActionType ?
renderAction(bookingDetailsActionType, props) :
renderActionButtons(props)
}
</div>
</div>
</Modal>
);
}
BookingDetailsModal.propTypes = {
orderId: PropTypes.string.isRequired,
hideOrderDetails: PropTypes.func.isRequired,
bookingDetailsActionType: PropTypes.oneOf([
'AMEND_CONSUMER_DETAILS',
]),
};
const mapStateToProps = (state, { orderId }) => (
{
ui: { bookingDetailsActionType },
ui: { isSuperAdmin },
orders: {
data: { [orderId]: order },
edits: { [orderId]: orderEdits },
},
}
) => ({
bookingDetailsActionType,
isSuperAdmin,
order,
isCreatingPayment: orderEdits.isCreatingPayment,
});
const mapDispatchToProps = (dispatch, { orderId }) => ({
hideOrderDetails: () => dispatch({ type: BOXOFFICE_HIDE_BOOKING_DETAILS }),
setActionType: actionType =>
dispatch({ type: BOXOFFICE_SET_BOOKING_DETAILS_ACTION_TYPE, actionType }),
resendConfirmationEmail: () => dispatch(resendConfirmationEmailActionCreator(orderId)),
payReservationCard: () => dispatch(payReservationCardActionCreator(orderId)),
payReservationCash: () => dispatch(payReservationCashActionCreator(orderId)),
payRest: type => dispatch(payRestActionCreator(orderId, type)),
refundCash: amount => dispatch(refundCashActionCreator(orderId, amount)),
cancelReservation: () => dispatch(cancelReservationActionCreator(orderId)),
});
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(s)(BookingDetailsModal));
My Redux tab on page load shows the following:
type(pin): "BOXOFFICE_IS_SUPER_USER"
isSuperAdmin(pin): true
This is how I have used createStore to access the variable:
const isSuperAdmin = createStore(null, {
[BOXOFFICE_IS_SUPER_USER]: isSuperAdmin => isSuperAdmin,
});
I then proceeded to add it to the reducer at the bottom.
edit I have changed the variable isSuperAdmin in the createStore to true and this can be read perfectly fine, it must now be an issue with the variable passed to the action in the first place.
Here is the code where I get the value of the variable and pass it on:
Export default ({ knex }) => authenticateAdmin(knex)(
async (req, res) => {
try {
const { eventId } = req;
const event = await fetchEvent(knex, eventId);
const isSuperAdmin = await res.isSuperAdmin;
res.send({ event, isSuperAdmin});
} catch (err) {
res.send(err.stack);
console.error(err.stack); // eslint-disable-line no-console
throw err;
}
}
);
And the dispatch:
export const fetchEvent = () => async (dispatch, getState) => {
try {
const state = getState();
const { auth: { password } } = state;
const response = await fetch('/api/event', {
headers: {
Accept: 'application-json',
'X-Password': password,
},
});
if (response.status === 200) {
const { event, isSuperAdmin } = await response.json();
dispatch({ type: BOXOFFICE_SET_EVENT, event });
dispatch({ type: BOXOFFICE_IS_SUPER_USER, isSuperAdmin });
} else {
localStorage.removeItem('password');
dispatch({ type: BOXOFFICE_UNAUTHENTICATE });
}
} catch (err) {
console.log(err); // eslint-disable-line no-console
throw err;
}
};
EDIT
Here is the reducer:
export default combineReducers({
isSuperAdmin, ------- My variable
isProcessingPayment,
isSelectDateCollapsed,
isLoadingBookings,
shouldShowBookings,
shouldShowDepositModal,
shouldShowReservationModal,
shouldShowConsumerDetailsModal,
shouldShowDiscountModal,
shouldShowOrderConfirmationModal,
bookingFilter,
selectedOrderId,
sendConfirmationEmail,
bookingIds,
orderDetailsId,
bookingDetailsActionType,
});
I guess the way you defined your mapStateToProps is incorrect.
Updated the code
try following:
const mapStateToProps = ({
ui: {
bookingDetailsActionType,
isSuperAdmin
},
orders: {
data,
edits
}
}, {
orderId
}) => {
const order = data[orderId],
orderEdits = edits[orderId];
return {
bookingDetailsActionType,
isSuperAdmin,
order,
isCreatingPayment: orderEdits.isCreatingPayment
};
};
I finally have a solution! Turns out my issue was not setting a property type for my isSuperUser variable. Despite my colleague telling me that it will work without any property type (which still makes sense to me and confuses me as to why it wont work?!).
A simple change in the index.js file from:
[BOXOFFICE_IS_SUPER_USER]: isSuperAdmin => isSuperAdmin,
to
[BOXOFFICE_IS_SUPER_USER]: (state, { isSuperAdmin }) => isSuperAdmin,
and adding a property type to the show.js file where I used res.send()
res.send({ event, isSuperAdmin: isSuperAdmin});
Im still at a loss as to why it won't work with no property type but oh well...!