Is this intentional?
When I declare a ref with default value inside a pinia defineStore (and then return it) when I access it from a component is always undefined, until I perform operations on it.
The store:
import {defineStore} from "pinia";
import {ref} from "vue";
export const useSelection = defineStore("selection", () => {
const contents = ref([]);
return {
contents
};
});
The component:
<script>
import { defineComponent, computed } from "vue";
import { useSelection } from "../store/selection.js";
export default defineComponent({
name: "Test",
setup() {
const selection = useSelection();
const thereIsSelection = computed(() => {
return selection.contents.value.length > 0;
})
return {
thereIsSelection
};
},
});
</script>
With this code I always get
Cannot read properties of undefined (reading 'length') as selection.contents.value is undefined
Is this normal? Best way to solve it? A computed instead of direct access to selection.contents that returns an array if selection.contents is undefined?
You should do return selection.contents.length > 0; instead of return selection.contents.value.length > 0;
const thereIsSelection = computed(() => {
return selection.contents.length > 0;
});
selection.contents is the actual value you need, not a reference.
firstly
value should be values in
selection.contents.value.length
like this
selection.contents.values.length
secondly
for me, using ref([]) was giving me this error
Uncaught TypeError: <my state>.<my array> is undefined
changing ref([]) to ref(new Array()) made the error go away
Documentation or an example on 'next-session' on how to connect it to Postgres was very dry.
Following the compatibility example on npm did not work.
const session = require("express-session");
const RedisStore = require("connect-redis")(session);
// Use `expressSession` from `next-session/lib/compat` as the replacement
import { expressSession } from "next-session/lib/compat";
const pgSession = require("connect-pg-simple")(expressSession)
export const getSession = nextSession({
cookie: {
maxAge: 432000,
},
store: new pgStore({...config}},
});
Error: ...failed to prune "session"
Not as simple as 'express-session' to get it working but it was a lot simpler than learning how to use jwt 'iron-session' library...
I managed to get it working by doing this:
import nextSession from "next-session";
import pgPool from "./db";
import { promisifyStore } from "next-session/lib/compat";
import { expressSession } from "next-session/lib/compat";
const pgSession = require("connect-pg-simple")(expressSession);
const connectStore = new pgSession({
pool: pgPool,
tableName: "session",
});
export const getSession = nextSession({
cookie: {
maxAge: 432000,
},
store: promisifyStore(connectStore),
});
I have a state and I'd like to create selectors from ngrx/data entities.
import {
Action,
ActionReducer,
ActionReducerMap,
createFeatureSelector,
createSelector,
MetaReducer
} from '#ngrx/store';
import {environment} from '../../environments/environment';
import * as fromRouter from '#ngrx/router-store';
import * as fromDrawer from './drawer';
import {InjectionToken} from '#angular/core';
import {NavigationItem} from '../models/navigation-item';
export interface State {
router: fromRouter.RouterReducerState<any>;
drawerNavigationItems: fromDrawer.State;
}
export const ROOT_REDUCERS = new InjectionToken<ActionReducerMap<State, Action>>('Root reducers token', {factory: () => ({
router: fromRouter.routerReducer,
drawerNavigationItems: fromDrawer.reducer,
}),
});
export const metaReducers: MetaReducer<State>[] = !environment.production ? [] : [];
export const selectRouter = createFeatureSelector<
State,
fromRouter.RouterReducerState<any>
>('router');
const {
selectQueryParams, // select the current route query params
selectQueryParam, // factory function to select a query param
selectRouteParams, // select the current route params
selectRouteParam, // factory function to select a route param
selectRouteData, // select the current route data
selectUrl, // select the current url
} = fromRouter.getSelectors(selectRouter);
export const selectRouteId = selectRouteParam('id');
export const selectStatus = selectQueryParam('status');
// Drawer
export const selectDrawerNavigationItems = (state: State) => state.drawerNavigationItems.items as NavigationItem[];
How do I use the pre-defined selectors or write my own with entities or services that came from ngrx/data?
As an example I'd like to create a selector that selects all "Community" entities and then, step 2, select 1 by selectRouteId.
If you imagine a route /communities/:id, selectRouteId returns the Id, and now I'd like the data from the CommunityService and use the selector created or somehow imported and used in step 1 and return a result, 1 Community with the selectRouteId's id, so I can later do something like this.store.dispatch(selectCommunityByCurrentRouteId);
This question is specific to #ngrx/data.
a supplementary answer, to concretely answer my own question,
here's what the reducers/index.ts looks like now
import {
Action,
ActionReducer,
ActionReducerMap,
createFeatureSelector,
createSelector,
MetaReducer
} from '#ngrx/store';
import {environment} from '../../environments/environment';
import * as fromRouter from '#ngrx/router-store';
import * as fromDrawer from './drawer';
import {InjectionToken} from '#angular/core';
import {NavigationItem} from '../models/navigation-item';
import {EntitySelectorsFactory} from '#ngrx/data';
import {Community} from '../models/community';
export interface State {
router: fromRouter.RouterReducerState<any>;
drawerNavigationItems: fromDrawer.State;
}
export const ROOT_REDUCERS = new InjectionToken<ActionReducerMap<State, Action>>('Root reducers token', {factory: () => ({
router: fromRouter.routerReducer,
drawerNavigationItems: fromDrawer.reducer,
}),
});
export const metaReducers: MetaReducer<State>[] = !environment.production ? [] : [];
export const selectRouter = createFeatureSelector<
State,
fromRouter.RouterReducerState<any>
>('router');
const {
selectQueryParams, // select the current route query params
selectQueryParam, // factory function to select a query param
selectRouteParams, // select the current route params
selectRouteParam, // factory function to select a route param
selectRouteData, // select the current route data
selectUrl, // select the current url
} = fromRouter.getSelectors(selectRouter);
export const selectRouteId = selectRouteParam('id');
// export const selectStatus = selectQueryParam('status');
// Data
export const communitySelectors = new EntitySelectorsFactory().create<Community>('Community');
export const selectCommunityByRouteId = createSelector(
selectRouteId,
communitySelectors.selectEntities,
(id, communities) => communities.find(c => c.id === id)
);
// Drawer
export const selectDrawerNavigationItems = (state: State) => state.drawerNavigationItems.items as NavigationItem[];
You create a selector for the Community model with
export const communitySelectors = new EntitySelectorsFactory().create<Community>('Community');
and then you combine those two and return 1 Community by the route id.
export const selectCommunityByRouteId = createSelector(
selectRouteId,
communitySelectors.selectEntities,
(id, communities) => communities.find(c => c.id === id)
);
really simple, you pick the input streams, provide a projection function and return the result.
Later, in the component
export class OneCommunityComponent implements OnInit {
community$: Observable<Community>;
constructor(
private store: Store<State>,
) {
this.community$ = this.store.select(selectCommunityByRouteId);
}
}
See https://github.com/peterbsmith2/platform/blob/b2f17bfcc987bf63d10dd207263c0ca2a2e44373/projects/ngrx.io/content/guide/data/extension-points.md#custom-selectors.
/* src/app/reducers/index.ts */
import * as fromCat from './cat.reducer';
import { Owner } from '~/app/models'
export const ownerSelectors = new EntitySelectorsFactory().create<Owner>('Owner');
export interface State {
cat: fromCat.State;
}
export const reducers: ActionReducerMap<State> = {
cat: fromCat.reducer
};
export const selectCatState = (state: State) => state.cat;
export const {
selectAll: selectAllCats
} = fromCat.adapter.getSelectors(selectCatState);
export const selectedCatsWithOwners = createSelector(
selectAllCats,
ownerSelectors.selectEntities,
(cats, ownerEntities) => cats.map(c => ({
...c,
owner: ownerEntities[c.owner]
}))
);
The best solution in your situation it's adapters is very nice.
You can look just here : https://ngrx.io/guide/entity/adapter
You can remove, add, update every object in your store with that and very easily.
You just need to extend your state :)
I try to config my saga store with Redux-saga, but I keep getting error about
Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers.
I did google around check the documentation, adjust my code still have the issue, so hoping someone can point out for me. Thanks!
if I change this code
const sagaMiddleware = ReduxSaga.default();
to
const sagaMiddleware =()=> ReduxSaga.default();
I got other error about: sagaMiddleware.run is not a function
Main.js
const { createStore, applyMiddleware } = require("redux");
const { createSelector } = require("reselect");
const ReduxSaga = require("redux-saga");
const createSagaMiddleware = require("redux-saga");
const reducer = require("./reducers/index");
const { workerSaga } = require("./sagas/sampleSaga.js");
const sagaMiddleware = ReduxSaga.default();
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(workerSaga);
Reducers/index.js
const { combineReducers } = require("redux");
const sample1 = require("./sample1");
const sample2 = require("./sample2");
const rootReducer = combineReducers({
sample1,
sample2
});
module.exports = rootReducer;
Reducers/sample1.js
const {
STARTS_LOADING_SAMPLE,
FETCH_SAMPLE,
COMPLETE_FETCHING_SAMPLE
} = require("../actions/index");
const sampleInitialState = {
fetching: false,
complete: false,
sample1: []
};
//switch statement....
module.exports = {
sample1: (allReducer, sampleInitialState)
};
The reducer is a pure function that takes the previous state and an action, and returns the next state. But you are exporting an object instead of a function in Reducers/sample1.js file. Try changing it to something like this:
...
module.exports = function(state = sampleInitialState, action) {
// switch statements
};
More on reducers: https://redux.js.org/basics/reducers
I am new on next js and i use redux-next-wrapper !
The problem is that i want to dispatch a token access but when i am doing it in getInitialProps, the store does not update after the render of the page !
I try to use componentDidMount, it work, but the state is update only after the render of the page, which make visible the button login one second before to be replace by logout !
componentDidMount () {
const token_access = Cookies.get('xxxxxxxxxx');
const token_refresh = Cookies.get('xxxxxxxxxx');
console.log(token_access);
if (token_access && token_refresh) {
const decode = jwt_decode(token_refresh);
if (decode.exp >= new Date()) {
this.props.store.dispatch(logout(token_refresh))
}
else {
const decode_access = jwt_decode(token_access);
if (decode_access.exp >= new Date()) {
refresh(token_refresh)
}
this.props.store.dispatch(userLoggedIn(token_access));
}
}
}
static async getInitialProps ({Component, ctx}) {
const token = '12345';
ctx.store.dispatch(userLoggedIn(token));
return {
pageProps: (Component.getInitialProps ? await Component.getInitialProps(ctx) : {})
}
}
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from '../reducers/index'
export default initialState => createStore(
rootReducer,
composeWithDevTools(
applyMiddleware(thunk)
)
);
is there a way to dispatch and load the store before the render of the page ?
Thank you for your answer
I fix this issue by changing the store ! Hope this help someone :)
export const initStore = (initialState = {} ) => {
return createStore(rootReducer,
initialState, composeWithDevTools(applyMiddleware(thunk)))
};