Expected server HTML to contain a matching <input> in <div> - next.js

Whenever I check for the existence of the condition isAuthorized (true or false) I get this error message on the console. Basically the error refers to server-side rendering, but I can't identify where I should fix it. When i remove isAuthorized the error disappears.
I have my context, I return isAuthorized as true or false depending on the existence of the token in the cookie.
GlobalContext.js
const Global = createContext();
export function GlobalContextProvider({ children }) {
const { push } = useRouter();
const userCookie = Cookies.getJSON("user");
const [authUser, setAuthUser] = useState(() => {
if (userCookie) return userCookie;
if (!userCookie) return {};
});
const [isAuthorized, setIsAuthorized] = useState(() => {
return !authUser || Object.keys(authUser).length == 0 ? false : true;
});
useEffect(() => {
if (!isAuthorized) {
return push("/auth/login");
}
}, [isAuthorized]);
const value = {
isAuthorized,
setIsAuthorized,
authUser,
setAuthUser,
};
return <Global.Provider value={value}>{children}</Global.Provider>;
}
export const useGlobalContext = () => useContext(Global);
Index.jsx
export default function PanelLayout({ children, title }) {
const { push } = useRouter();
const { isAuthorized, setIsAuthorized } = useGlobalContext();
useEffect(() => {
const userData = Cookies.getJSON("user");
if (!userData || !userData.access_token) {
return push("/auth/login");
setIsAuthorized(false);
}
}, []);
return (
<>
{isAuthorized && (
<>
<Head>
// Content
</Head>
<input type="checkbox" id="sidebar-toggle" />
<div className="main-content">
// Content
</div>
</>
)}
</>
);
}

use this code in routes file where all the routes with components are placed.
and place this useEffect there if this condition meets give the access to that routes otherwise redirect it to login.
useEffect(() => {
const userData = Cookies.getJSON("user");
if (!userData || !userData.access_token) {
return push("/auth/login");
setIsAuthorized(false);
}
}, []);

Related

Next.js role based access to component

I'm trying to restrict access to a admin area based on a int flag called isAdmin that is set to either 0 or 1 in my users table.
In the admin component I've made a function to fetch an API route that returns a unique user based on email, which will allow me to pass this parameter from the session to the API route - but it never returns true value
This is the code for the lookup function and how I restrict access in the component
export const getServerSideProps: GetServerSideProps = async () => {
const dashboards = await prisma.dashboard.findMany({
orderBy: {
id: "asc",
}
})
return {
props: JSON.parse(JSON.stringify({ dashboards })),
}
}
async function checkAdminUser(email: string) {
try {
const result = await fetch(`/api/user/${email}`, {
method: "GET",
})
const user = await result.json()
if (user.isAdmin == 1) {
return true
} else {
return false
}
} catch (error) {
console.error(error)
}
}
const Dashboard: React.FC<Props> = (props) => {
const { data: session, status } = useSession()
if (!session || !checkAdminUser(session.user?.email)) {
return (
<Layout>
<AccessDenied />
</Layout>
)
}
return (
<Layout>
..Layout code
</Layout>
)
}
I've also tried the checkAdminUser function as a Promise without success. The API route has been checked for valid output
"{"id":1,"image":null,"name":null,"email":"censoredfor#crawlers.com","emailVerified":null,"isAdmin":1,"createdAt":"2022-09-21T07:52:20.263Z","updatedAt":"2022-09-21T10:22:39.024Z"}"
Any tips to get me rolling would be greatly appreciated!
This answer assumes that console.log(user) gives you: {"id":1,"image":null,"name":null,"email":"censoredfor#crawlers.com","emailVerified":null,"isAdmin":1,"createdAt":"2022-09-21T07:52:20.263Z","updatedAt":"2022-09-21T10:22:39.024Z"}
Then you should better handle how you check if the user is an Admin. checkAdminUser returns a promise that is not resolved when you're checking for the value. A better solution would be to use react state to manage access to a specific component:
const Dashboard: React.FC<Props> = (props) => {
const { data: session, status } = useSession()
const [isAdmin, setAdmin] = useState(false);
const checkAdminUser = useCallback(async () => {
try {
const result = await fetch(`/api/user/${session?.user?.email}`, {
method: "GET",
})
const user = await result.json()
if (user.isAdmin == 1) {
setAdmin(true)
} else {
setAdmin(false)
}
} catch (error) {
console.error(error)
}
},[session?.user?.email])
useEffect(() => {
checkAdminUser()
},[checkAdminUser])
if (!session || !isAdmin) {
return (
<Layout>
<AccessDenied />
</Layout>
)
}
return (
<Layout>
..Layout code
</Layout>
)
}
Don't forget to: import {useCallback, useEffect, useState} from 'react'

TypeError: dispatch is not a function when clicking the toggle button

I am using react redux-thunk. I have a set of users data that I get from an API and this is the schema:
.
I've connected the "active" property with the checked attribute of a Switch MUI button, so naturally when calling the API I have some users with their switch button already on "true". What I am trying to do is to just make the switch functional, and just be able to click it and change its state, not necessarily doing anything with that.
Here's my toggleType.js:
export const TOGGLE = "TOGGLE";
Here's my toggleAction.js:
import { TOGGLE } from "./toggleType";
const statusToggleAction = () => {
return {
type: TOGGLE,
};
};
export const statusToggle = () => {
return (dispatch) => {
dispatch(statusToggleAction);
};
};
Here's my toggleReducer.js:
import { TOGGLE } from "./toggleType";
const initialState = {
status: false,
};
const toggleReducer = (state = initialState, action) => {
switch (action.type) {
case TOGGLE:
status: true;
default:
return state;
}
};
export default toggleReducer;
Everything is under my userContainer.js, like that:
function UserContainer({ userData, fetchUsers }) {
useEffect(() => {
fetchUsers();
}, []);
return userData.loading ? (
<h2>Loading</h2>
) : userData.error ? (
<h2>{userData.error}</h2>
) : (
<Container maxWidth="lg" style={{ flexGrow: 1, height: "100%" }}>
<h2>User List</h2>
<div>
{userData &&
userData.users &&
userData.users.map((user) => (
<div key={user.id}>
<p>{user.name}</p>
<Switch checked={user.active} onChange={statusToggle()} />
</div>
))}
</div>
</Container>
);
}
const mapStateToProps = (state) => {
return { userData: state.user, statusToggle: state.status };
};
const mapDispatchToProps = (dispatch) => {
return {
fetchUsers: () => dispatch(fetchUsers()),
statusToggle: () => dispatch(statusToggle()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserContainer);
This is the error I am getting whenever I am clicking one of those switches:
Any ideas are welcome, I "learned" redux like 3 days ago!
toggleReducer function in toggleReducer.js, replace status: true; with return { status: true }.
Just return action in statusToggle function in toggleAction.js without dispatch as following.
export const statusToggle = () => {
return statusToggleAction();
};
Or just call statusToggleAction directly in userContainer.js as following.
export const statusToggle = () => {
return (dispatch) => {
dispatch(statusToggleAction());
};
};

How to update url img in firebase

I have a todo list, where I want add some fields:
name
description
I also have two other fields to add:
icon
logo
My image is stored in firebase storage, on this level is worked well. My problem is that when I try to update the card, it only updated the name and description, but the field url remains empty. I am developing on react js and using hooks.
Where do I have an issue? How can I resolve it?
here is an example of my code
const Menu = ({ add, updateCard, contactId }) => {
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [uploadFile, setUploadFile] = useState(null);
const [url, setImageUrl] = useState('');
const handleChange = useCallback(({ target }) => {
const { name, value } = target;
switch (name) {
case 'name':
setName(value);
break;
case 'description':
setDescription(value);
break;
case 'icon':
setUploadFile(target.files[0]);
break;
case 'logo':
setUploadFile(target.files[0]);
break;
default:
return;
}
}, []);
const resset = () => {
setName('');
setDescription('');
};
const handleSubmit = e => {
e.preventDefault();
resset();
};
useEffect(() => {
if (uploadFile?.name) {
AsynchRoneUploadFile();
}
async function AsynchRoneUploadFile() {
const storageRef = app.storage().ref('Logo');
const fileRef = storageRef.child(uploadFile?.name);
try {
await fileRef.put(uploadFile);
setImageUrl(await fileRef.getDownloadURL());
} catch (e) {
console.log(e.message);
}
}
}, [handleChange, updateCard, uploadFile]);
const buttonName = (add && 'Creat') || (updateCard && 'Update');
return (
<>
<form onSubmit={handleSubmit}>
<Form
handleChange={handleChange}
name={name}
description={description}
/>
<UploadBar onHandlechange={handleChange} />
<button
className={s.btn}
type="submit"
onClick={() =>
(add && add({ name, description, url })) ||
(updateCard &&
updateCard({
contactId,
name,
description,
url,
}))
}
>
{buttonName}
</button>
</form>
</>
);
};

too many renders react hooks, useEffect, map

hey guys I'm trying to add to sum a simple product price for each iteration and notice its runs too manytimes. in the use effect I have to listen to the UserCart. can't do it with an empty array/[userCart].
also I get the following error in more components which i couldnt fix for the same reason with the last argument in the useEffect:
update: I did divide the useEffect as suggusted but it with [userCart ] it enter infinite loop
the code:
import React, { useState, useEffect } from 'react'
import firebase from 'firebase';
import { useAuth, useStoreUpdate } from '../contexts/FirebaseContext';
export default function Cart() {
const [userMail, setUserMail] = useState(undefined)
const [userCart, setUserCart] = useState(undefined)
const [totalAmmout, setTotalAmmout] = useState(0)
const user = useAuth()
const userDoc = firebase.firestore().collection("cart").doc(userMail)
const updateStore = useStoreUpdate()
const updateCart = () => {
userDoc.get().then((doc) => {
if (doc.exists) {
let cart = doc.data()
setUserCart(cart)
}
})
}
async function removeFromCart(itemId, name, url, price, category, type, description) {
const cartItem = { itemId, name, url, price, category, type, description }
await userDoc.update({
item: firebase.firestore.FieldValue.arrayRemove(cartItem)
})
await updateCart()
await updateStore()
}
useEffect(() => {
if (user.currentUser) {
setUserMail(user.currentUser.email)
updateCart()
updateStore()
}
},userCart)
if (!userCart) return <h1>hold</h1>
let total = 0
return (
<main className="main-cart">
<div className="container">
{userCart.item && userCart.item.length >= 1 && userCart.item.map((item, i, arr) => {
console.log(item); //it runs more than 20 times
return (
< div className="item-container" key={item.itemId} >
<h3>{item.name}</h3>
<p>{item.price}</p>
<img height="150px" width="150px" src={item.url} alt="" />
<button onClick={async () => {
await removeFromCart(item.itemId, item.name, item.url, item.price, item.category, item.type, item.description)
}}>X</button>
</div>
)
})}
</div>
<div className="fixed-bottom-link">
<button>finish purchase</button>
</div>
</main >
)
}
edit :
I found the major component, but still couldnt make it work
the 2nd useEffect [userCart] wont run, it works only when not in the array, but it enter a loop:
import React, { useContext, useState, useEffect } from 'react';
import { auth } from '../firebase/firebase';
import firebase from '../firebase/firebase';
const FirebaseContext = React.createContext()
const StoreContext = React.createContext()
const StoreContextUpdate = React.createContext()
export function useAuth() {
return useContext(FirebaseContext)
}
export function useStore() {
return useContext(StoreContext)
}
export function useStoreUpdate() {
return useContext(StoreContextUpdate)
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true)
const [userMail, setUserMail] = useState(undefined)
const [userCart, setUserCart] = useState(undefined)
const userDoc = firebase.firestore().collection("cart").doc(userMail)
const updateCart = () => {
userDoc.get().then((doc) => {
if (doc.exists) {
let cart = doc.data()
setUserCart(cart)
}
})
}
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password)
}
function login(email, pasword) {
return auth.signInWithEmailAndPassword(email, pasword)
}
function logout() {
return auth.signOut()
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async user => {
await setCurrentUser(user)
await setLoading(false)
})
return unsubscribe
}, [userCart])
useEffect(()=>{
async function refreshCart(){
await setUserMail(currentUser.email);
await updateCart()
}
if (currentUser && currentUser.email) {
refreshCart()
}
return
},[userCart])
const value = {
currentUser,
signup,
login,
logout
}
return (
<FirebaseContext.Provider value={value}>
<StoreContext.Provider value={userCart && userCart.item}>
<StoreContextUpdate.Provider value={updateCart}>
{!loading && children}
</StoreContextUpdate.Provider>
</StoreContext.Provider>
</FirebaseContext.Provider>
)
}
useEffect(() => {
if (user.currentUser) {
setUserMail(user.currentUser.email);
updateCart();
updateStore();
}
}, [userCart]); //<-- userCart needs to be inside array
and the reason for too many rerenders is you're calling updateCart function when the userCart changes and then userCart change will cause the call of updateCart again and this loop will repeat itself until you're out of memory
I think adding another useEffect might help you
useEffect(() => {
if (user.currentUser) {
setUserMail(user.currentUser.email);
updateStore();
}
}, [userCart]); //<-- userCart needs to be inside array
useEffect(() => {
if (user.currentUser) {
updateCart();
}
}, []);

TypeError: defaultPieces inside of componentDidMount is not a function

Inside of my PossibleMatches component, I have imported a few asynchronous functions from my actions/index file and as I call these functions inside of componentDidMount, the Error I'm getting back is: defaultPieces is not a function.
Below are the contents of my PossibleMatches component and actions/index.js file:
For the sake of brevity, I did my best to add everything that is relevant to the main problem.
PossibleMatches.js
import { connect } from 'react-redux';
import {arrangePieces, defaultPieces, setEvaluatedPiece, getCorrespondingPieces} from '../actions/index';
constructor(props){
super(props);
const initialState = {
currentComponent: {whichPiece: {whichType: null, currentUpperComponent: null, currentLowerComponent: null}},
UpperComponents: this.props.UpperComponents,
LowerComponents: this.props.LowerComponents,
UpperComponentEnabled: false,
LowerComponentEnabled: false,
isFetched: false,
isFetching: true
}
this.state = {
...initialState
}
this.residingUpperComponent = createRef();
this.residingLowerComponent = createRef();
//Also need to this.prop.setEvaluatedPiece based off of this.props.setCurrentComponent if callback from Lower or Upper Components elapses time
this.setNewPiece = this.setNewPiece.bind(this);
this.renderDecision = this.renderDecision.bind(this);
}
async componentDidMount(){
const {store} = this.context;
let stateRef = store.getState();
const { defaultPieces, arrangePieces } = this.props;
try {
const summon = () => { defaultPieces();
arrangePieces();}
await summon();
} catch(error){
throw Error(error);
}
console.log("AFEWOVMAOVHIE")
this.setState({isFetched: true, isFetching: false});
}
renderDecision(){
const { currentComponent, LowerComponentEnabled, UpperComponentEnabled, isFetching, isFetched} = this.state;
const { suggestedBottoms, suggestedTops, UpperComponents, LowerComponents } = this.props;
if (isFetching){
return (<div className='activityLoader'>
<ActivityIndicator number={3} duration={200} activeColor="#fff" borderWidth={2} borderColor="50%" diameter={20}/>
</div>);
} else if (isFetched){
return (
<div className = "PossibleMatches_Container">
<i className = 'captureOutfit' onClick = {this.snapshotMatch}></i>
<TransitionGroup component = {PossibleMatches}>
** ** ** {UpperComponents.map((component) => {
return (<UpperComponent key={component.created_at} id={component.id}
switchComponent={this.switchFocus}
setCurrentPiece={this.setNewPiece}
evaluatePiece={this.isOppositeComponentSuggested}
image={component.image}
toggleToPiece = {() => {if (LowerComponentEnabled === false){this.setState({LowerComponentEnabled: true})} else return; this.setState({currentLowerComponent: suggestedBottoms[0]})}}
isLowerComponentEnabled = {LowerComponentEnabled}
ref = {this.residingUpperComponent}
className = {currentComponent.whichPiece.whichType === 'match' ? 'PossibleMatches_Container' : currentComponent.whichPiece.whichType === 'bottom' ? 'standalonePiece' : 'standalonePiece'}/>
)
})
}
</TransitionGroup>
<TransitionGroup component = {PossibleMatches}>
{LowerComponents.map((component) => {
return (<LowerComponent key={component.created_at} id={component.id}
setCurrentPiece = {this.setNewPiece}
evaluatePiece={this.isOppositeComponentSuggested}
image={component.image}
toggleToPiece = {() => {if (UpperComponentEnabled === false){this.setState({UpperComponentEnabled: true})} else return; this.setState({currentUpperComponent: suggestedTops[0]})}}
switchComponent = {this.switchFocus}
isUpperComponentEnabled = {UpperComponentEnabled}
ref = {this.residingLowerComponent}
className = {this.state.currentComponent.whichPiece.whichType === 'match' ? 'PossibleMatches_Container' : this.state.currentComponent.whichPiece.whichType === 'bottom' ? 'standalonePiece' : 'standalonePiece'}/>)
})
}
</TransitionGroup>
</div>
)
}
}
render(){
return(
<div className = 'GorClothingContainer'>
<Wardrobe upperComponent={this.state.currentComponent.whichPiece.currentUpperComponent} lowerComponent={this.state.currentComponent.whichPiece.currentLowerComponent} enableCapture={(snapshot) => this.snapshotMatch = snapshot} />
{this.renderDecision()}
</div>
);
}
function mapStateToProps(state){
const { UpperComponents, LowerComponents, contemplated_piece, extraTops, extraBottoms, standaloneBottoms, standaloneTops, suggestedBottoms, suggestedTops } = state.possibleMatches;
return {UpperComponents, LowerComponents, contemplated_piece, extraTops, extraBottoms, standaloneBottoms, standaloneTops, suggestedBottoms, suggestedTops };
}
export default connect(mapStateToProps, {defaultPieces, arrangePieces, getCorrespondingPieces, setEvaluatedPiece})(PossibleMatches)
Inside of my actions/index.js
export function defaultPieces(){
return function(dispatch){
fetch(`${API_URL}/possible_matches/setup_possible_matches`, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}).then((res) => res.json())
.then((json) => {
console.log('defaultPieces: ', json);
dispatch(getInitialPieces(json))
})
}
}
export function getInitialPieces(request){
return {
type: INITIAL_PIECES,
payload: request
}
}
Inside of PossibleMatches reducer:
import {INITIAL_PIECES, GET_ANCILLARY_PIECES, ORGANIZE_PIECES, SET_CONTEMPLATED_PIECE} from '../actions/types';
const initialState = {
UpperComponents: [],
LowerComponents: [],
contemplated_piece: null,
extraTops: [],
extraBottoms: [],
standaloneTops: [],
standaloneBottoms: [],
suggestedTops: [],
suggestedBottoms: []
}
export default function(state = initialState, action){
switch(action.type){
case INITIAL_PIECES:
return {...state, {contemplated_piece: action.payload.contemplated_piece,
extraTops: action.payload.extra_tops,
extraBottoms: action.payload.extra_bottoms,
standaloneTops: action.payload.standalone_tops,
standaloneBottoms: action.payload.standalone_bottoms,
suggestedTops: action.payload.suggested_tops,
suggestedBottoms: action.payload.suggested_bottoms}
case GET_ANCILLARY_PIECES:
return {...state, extraTops: action.payload.extra_tops,
extraBottoms: action.payload.extra_bottoms,
standaloneTops: action.payload.standalone_tops,
standaloneBottoms: action.payload.standalone_bottoms,
suggestedTops: action.payload.suggested_tops,
suggestedBottoms: action.payload.suggested_bottoms}
case ORGANIZE_PIECES:
return {...state, UpperComponents: action.payload.UpperComponents,
LowerComponents: action.payload.LowerComponents}
case SET_CONTEMPLATED_PIECE:
return {...state, contemplated_piece: action.payload.contemplated_piece}
default:
return state;
}
}
Because defaultPieces is not a valid function to the PossibleMatches components, it interferes with the interpretation of the UpperComponents prop that comes from mapStateToProps function (denoted with an * above).
What is peculiar is the json logged out to the console from both the arrangePieces and defaultPieces methods:
It was a bizarre fix, but I basically needed to set conditions in ComponentDidMount to halt the program based on the state of UpperComponents relative to what got returned from this.props.arrangePieces().
if (UpperComponents.length === 0){
return;
} else {
this.setState({isFetched: true, isFetching: false});
}
Since the component rerenders, I thought it was idealistic to add a componentWillRecieveProps lifecycle method to deal with incoming props:
componentWillReceiveProps(nextProps){
if (nextProps.contemplated_piece !== this.props.contemplated_piece){
if (nextProps.contemplated_piece.merch_type === 'top'){
this.setState({currentLowerComponent: nextProps.suggestedBottoms[0],
currentUpperComponent: nextProps.contemplated_piece});
}
else if (nextProps.contemplated_piece.merch_type === 'bottom'){
this.setState({currentLowerComponent: nextProps.contemplated_piece,
currentUpperComponent: nextProps.suggestedTops[0]});
}
}
else {
return null;
}
}

Resources