How do you access a session in an endpoint in sveltekit? I've tried this but no luck:
import { get } from 'svelte/store';
import { getStores} from "$app/stores";
function getUser() { // <- call this at component initialization
const { session } = getStores();
return {
current: () => get(session).user
}
}
The session store is being populated in src/hooks.js, the normal flow to do so is
in handle, add some data to event.locals.
in getSession, use event.locals to create a session object.
This session object is available in the client as the session store, and on during ssr if you use the load functions, but it is not available in endpoints.
What is available though in the endpoint is the locals variable, that is originally passed to the getSession function, so you can read that one.
export async function get({ locals }) {
// code goes here
}
Just be aware that this means there is no synchronization between locals and the client side session, if you add something to the session it will not be available to the endpoint. To handle this you would have to for example add new data to the cookie and parse this in the handle function.
The session store only works inside svelte components, (it uses context under the hood) this provides isolation between users.
You can import the getSession from src/hooks.js and pass the event to reuse the logic that extracts session data from the request.
Related
I want to define a global array that I can set in getserverprops and use throughout the project. Is this possible in next js?
I'm going to use this array as a cache
You can use a global variable on the server (getServerSideProps),
and
you also can use a global variable on the client (aka. the browser),
but
you can not use the same global variable on the server and the client.
Server only
If you only ever want to access that variable inside getServerSideProps,
then that is theoretically possible, but likely will cause all sorts of problems. E.g. consider a load balancer and 3 different server instances,
which have 3 different caches.
Better would be to use some "established caching technology", like a Redis DB.
Shared values
As said, you can not share a variable between server and client.
You might (as a mental model) consider getServerSideProps to be executed in a different country
on some secured server which you don't have access to, while the rest of the components (not all of them)
are executed on your computer in your browser.
So if you want to share some state between client and server, you need to create an API on the server, and communicate between client and server through this API.
If you just define a global array, that array will be created and can be used, but it will be created independently on the server and on the client, i.e. they will be two completely different variable.
my-app/global.js:
export const globalVariable = {
trace: [],
};
Then you access this variable inside the index.tsx:
my-app/pages/index.jsx:
const Home = ( props ) => {
console.log('Client: globalVariable', globalVariable);
console.log('Client: pageProps:', props);
useEffect(() => {
globalVariable.trace.push('from MyApp');
}, []);
return null;
}
export async function getServerSideProps() {
globalVariable.trace.push('from getServerSideProps');
return {
props: {
serverVariable: globalVariable,
},
}
}
Then you will have one globalVariable on the client, and a separate globalVariable on the server.
You will never see "from getServerSideProps" on the client, you will never see "from MyApp" on the server.
You can pass globalVariable from the server as props, like I did with serverVariable: globalVariable,
and that value will be available on the client, but it will be a third new variable on the client side.
You can not hope to props.serverVariable.trace.push('pushed from client to server'), that will only push
to the new client variable.
I need to integrate redux with an existing next project. but currently, I don´t understand how the store works server-side.
I´m following this example:
https://github.com/vercel/next.js/blob/canary/examples/with-redux/pages/ssg.js
export const initializeStore = (preloadedState) => {
let _store = store ?? initStore(preloadedState)
// After navigating to a page with an initial Redux state, merge that state
// with the current state in the store, and create a new store
if (preloadedState && store) {
_store = initStore({
...store.getState(),
...preloadedState,
})
// Reset the current store
store = undefined
}
// For SSG and SSR always create a new store
if (typeof window === 'undefined') return _store
// Create the store once in the client
if (!store) store = _store
return _store
}
Why a store is created in the server, I´m asking is because in the client also is created so, what store is finally used.
Why do I need to create an store in the server if in the client another totally different is created?
import { Provider } from 'react-redux'
import { useStore } from '../store'
export default function App({ Component, pageProps }) {
const store = useStore(pageProps.initialReduxState)
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
)
}
The component definition below is also rendered in the client?
export function useStore(initialState) {
const store = useMemo(() => initializeStore(initialState), [initialState])
return store
}
It is for SEO purpose. When client makes a request to the server, if you fetch the data on the server, populate the store and send this data to the client, search engine crawlers will see what is your website about, they can read what you have sent.
But you do not need to fully populate the store on the server. For example, let's say you have an admin page, and admin can access to users of your app or website. So when yo fetch data on the server, your server will ship a bunch of names to the client and this will not effect on your SEO results. In this case, in admin page component, inside "useEffect" you can dispatch action to get the list of users from the server.
Let's say you have an ecommerce website and in index page you are showing your products. In this case, it will be better to populate the store on the server so search engine crawlers can read your products and it helps your SEO results. To sync the store with the client you also have to dispatch "hydrate" action from the client to pass the data to the client side store. I think in your code "initializeStore()" is handling that.
I have some general questions which are bothering me regarding the context API and local storage.
When to use local storage?, when to use the Context API and when would I use both?
I know that to persist data after the refresh I need something like local storage or session storage, so do I ditch the context API entirely and just store everything in the local storage? this way I can not only store data but also keep it on refresh? some insight would be really helpful.
What are some pros and cons?
Context API vs Local storage is apples vs oranges comparison.
Context API is meant for sharing state in the component tree.
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Local Storage is for storing data between sessions.
The read-only localStorage property allows you to access a Storage object for the Document's origin; the stored data is saved across browser sessions.
The right comparison might be Local Storage vs Cookies, Context API vs State-Management Library (not really, since Context is not a state management tool).
While you can store everything on the local storage (although it's not scalable and maintainable) it won't be useful.
It won't be useful because you can't notify your components on state change, you need to use any React's API for it.
Usually local storage is used for session features like saving user settings, favorite themes, auth tokens, etc.
And usually, you read once from local storage on application start, and use a custom hook to update its keys on related data change.
Here is a helpful recipe for useLocalStorage custom hook:
function useLocalStorage(key, initialValue) {
// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => {
try {
// Get from local storage by key
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// If error also return initialValue
console.log(error);
return initialValue;
}
});
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = value => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(storedValue) : value;
// Save state
setStoredValue(valueToStore);
// Save to local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
// A more advanced implementation would handle the error case
console.log(error);
}
};
return [storedValue, setValue];
}
So let's say I have this global JS function file that other components use to make rest calls, if the response is unauthorized I want it to change the state of a React component to loggedIn:false. Is this possible?
Looks like a use case of redux. You will maintain loggedIn in your store and connect whichever component requires this info with the store.
Else there are two other ways not that good which I will call hacks.
1) Maintain this in url query params and read the params in component.
2) Maintain this in sessionStorage or localStorage.
3) Maintain it in window.isLoggedIn
But since this is login related info I would avoid using these ways. For some other global, you can use above mentioned ways.
Take a look at redux here
https://redux.js.org/basics/usagewithreact
class SomeComponent extends React.Component {
...
async getData() {
await response = fnToGetData();
if (response.unauthorized) {
this.props.setAuthorizationStatus(false);
// or if you want to set authorization only in this component
// this.setState(() => ({authorized: false}));
} else {
// do sth with the data
}
}
}
Where setAuthorizationStatus lives somewhere on top of your app and is passed down through props (you can also use context to pass it down conveniently).
Suppose I am writing an application in Redux and I am tasked to add logging using a 3rd party library. Its API is as follows:
function createLogger(token) {
// the logger has internal state!
let logCount = 0;
return {
log(payload) {
logCount++; // modify local state
fetch('/someapi', { // ship payload to some API
method: 'POST',
body: payload
});
}
};
}
I would then use the library something like this:
let logger = createLogger('xyz');
logger.log('foobar');
I definitely want to create the logger instance just once during application init. But then the question is: where do I store the logger instance?
First instict is to put it somewhere in the store. But is that a good idea? As I have demonstrated in the code the logger object is stateful, it stores a counter in the closure. I do not get a new instance like I would with an immutable object. As we know, state should only be modified via pure reducer functions.
Other possibilities are to create the instance somewhere in a redux middleware closure or just create a global variable, which is obviously evil in terms of testability.
Is there a best practice for this (I would think) rather common scenario?
Since you are using ES6 modules I would setup your logger as a module, export it, and import it wherever you plan to use it. I think logging from the actions is a solid plan, since it keeps the components unaware, and doesn't pollute the store with side-effects.
function createLogger(token) {
// the logger has internal state!
let logCount = 0;
return {
log(payload) {
logCount++; // modify local state
fetch('/someapi', { // ship payload to some API
method: 'POST',
body: payload
});
}
};
}
export default const logger = createLogger('xyz');
Your action creators
import logger from 'logger-module';
//
logger.log('somestuff');
Testing is still easily achievable by importing the logger and placing whatever spy/stub on its methods that you need to intercept.
From the Redux documentation:
/**
* Sends crash reports as state is updated and listeners are notified.
*/
const crashReporter = store => next => action => {
try {
return next(action)
} catch (err) {
console.error('Caught an exception!', err)
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
})
throw err
}
}
Raven being a third-party library.
If the library has its own state then it shouldn't be an issue using it in middleware (the state belongs in the library and not your app). If you're creating a state for it, for some reason, then that state should belong in the Redux store, probably under store.logger or something.