I've got a Nuxt application with a store directory that resembles this folder
https://nuxtjs.org/docs/2.x/directory-structure/store#example-folder-structure.
Imagine that I have a property isComplete in my module state inside the cart folder.
I get the following error that Property 'shop' does not exist on type 'RootState'.
How could I access this property in a Vue component ?
Component.vue
<script lang="ts">
import { defineComponent, onMounted } from '#nuxtjs/composition-api'
import { useStore } from '~/store'
export default defineComponent({
name: 'Component',
setup() {
const store = useStore()
onMounted(() => {
if (store.state.shop.cart.isComplete) {
// Execute some code
}
})
},
})
</script>
My store/index.ts has the following implementation
import { InjectionKey, useStore as baseUseStore } from '#nuxtjs/composition-api'
export interface RootState {}
export const state = () => ({})
export const injectionKey: InjectionKey<RootState> =
Symbol('vuex-injection-key')
export const useStore = () => {
return baseUseStore(injectionKey)
}
store/shop/cart/state.ts
export interface CartState {
isComplete: boolean
}
export const state = (): CartState => ({
isComplete: false,
})
Store state
Store files named state should have an object-returning method as its default export, so store/shop/cart/state.ts should contain export default state (where state is the method) as its last line. Otherwise, you'll see a warning in the browser console:
store/shop/cart/state.ts should export a method that returns an object
Or you could rename store/shop/cart/state.ts to store/shop/cart/index.ts (perhaps the original intent based on the exported state constant).
RootState type
There's no type inference available here, so the RootState needs to be explicitly typed to include the states of modules.
Import the CartState from the namespaced module.
Add a key named after each namespaced module, nested as needed (i.e., shop and then cart). Apply the CartState type for the cart key.
// store/index.ts
import type { CartState } from './shop/cart' 1️⃣
export interface RootState {
shop: {
cart: CartState, 2️⃣
}
}
GitHub demo
Related
I am trying to define a global state provider for an app I am building with react. But I keep getting the error
The object passed as the value prop to the Context provider (at line 19) changes every render. To fix this consider wrapping it in a useMemo hook
Here is my file structure. state.ts
export default interface State {
data: boolean
}
export const initialState: State = {
data: false,
}
action.ts
type Action = {
type: "SET_DATA"
value: boolean
}
export default Action
context.ts
import { Context, createContext, Dispatch } from "react"
import Action from "./actions"
import State, { initialState } from "./state"
const GlobalContext: Context<{
globalState: State
dispatch: Dispatch<Action>
// eslint-disable-next-line #typescript-eslint/no-unused-vars
}> = createContext({ globalState: initialState, dispatch: (_: Action) => {} })
export default GlobalContext
provider.tsx
import * as React from "react"
import { ReactNode, ReactElement, useReducer } from "react"
import GlobalContext from "./context"
import Reducer from "./reducer"
import State, { initialState as defaultInitialState } from "./state"
export default function GlobalStateProvider({
children,
initialState = defaultInitialState,
}: {
children: ReactNode
initialState?: State
}): ReactElement {
const [globalState, dispatch] = useReducer(Reducer, initialState)
return (
<GlobalContext.Provider value={{ dispatch, globalState }}>
{children}
</GlobalContext.Provider>
)
}
GlobalStateProvider.defaultProps = {
initialState: defaultInitialState,
}
I have gone through the code multiple times and I cannot seem to figure out what is wrong and why I am getting this error.
If someone can further explain why this is happening and perhaps and solution that would be helpful.
This is not really a syntax error, meaning it won't break the app, but a linting error, where some linters are trying to enforce best standards. You can change linter settings to provide warnings instead of throwing errors.
If you want to fix this, you can wrap the context values in a useMemo hook as the linter suggested.
const globalContextValue = useMemo(
() => ({
dispatch, globalState
}),
[dispatch, globalState]
);
return (
<GlobalContext.Provider value={globalContextValue}>
{children}
</GlobalContext.Provider>
)
Note that there are controversies around this linting rule, some say that its outdated and unnecessary, so some would suggest to disable the rule entirely.
I'm trying to learn how to use hooks and context in React, but need to work with TypeScript (new to that as well).
I am confused about what to set provider value on my below code. I have created a hook useFirebase it will return some functions like RegisterUser, SignIn, user(it's a state), logout.
First I created UseAuth.tsx hook, using this hook I want to import the above functions which I mentioned.
import { useContext } from 'react';
import { AuthContext } from '../context/AuthProvider';
const UseAuth = () => {
const auth = useContext(AuthContext)
return auth
};
export default UseAuth;
Everything is ok but I am getting an error on the provider value. The error is (JSX attribute) React.ProviderProps<null>.value: null Type '{ RegisterUser: RegisterUserFunction; SignIn: SignInUserFunction; user: userState; logout: () => void; }' is not assignable to type 'null'. The expected type comes from property 'value' which is declared here on type 'IntrinsicAttributes & ProviderProps<null>' I have created an interface IAuth but where I need to define it I am confused.
import React, { createContext } from 'react';
import { UseFirebase } from '../hooks/UseFirebase';
export const AuthContext = createContext(null)
export interface IAuth {
RegisterUser: () => void;
SignIn : () => void;
user : any
logout : () => void;
}
const AuthProvider = ({ children } :any) => {
const allContext = UseFirebase()
return (
<AuthContext.Provider value={allContext}>
{children}
</AuthContext.Provider>
);
};
export default AuthProvider;
The problem is in
export const AuthContext = createContext(null)
You need to pass a proper type to it, so it won't complain when trying to assign any value.
Try something like
React.createContext<IAuth>(initialValue);
For a more detailed explanation check out this article react-context-with-typescrip
I have two slice reducers and I want to get the state of one to the other.
In the old way of redux I would do it like that:
import store from "store"
///reducer code
const state = store.getState();
Now Iam using redux-toolkit and this way of importing store doesnt work.
here is LoginSlice:
import { createSlice } from '#reduxjs/toolkit';
import axios from "axios"
import Swal from 'sweetalert2'
export const initialState = {
id: "1111"
}
export const LoginSlice = createSlice({
name: 'login',
initialState,
reducers: {
login: state => {
state.id = "2222"
},
},
});
export const { login} = LoginSlice.actions;
here is Sites slice :
import { createSlice } from '#reduxjs/toolkit';
import axios from "axios"
export const SitesSlice = createSlice({
name: 'sites',
initialState: {
sites: []
},
reducers: {
GetSites: (state,action) => {
axios.post(process.env.REACT_APP_API_URL+"/sites/get",{id: get id here}).then((err,data) =>{
if(data.data.success){
state.sites = data.data.data
}else{
error
}
})
.catch(error => {
error
})
}
},
});
export const { GetSites } = SitesSlice.actions;
here is the store:
import { configureStore } from '#reduxjs/toolkit';
import { combineReducers } from 'redux'
import loginReducer from '../features/Login/loginSlice';
import siteReducer from '../features/Sites/SitesSlice';
const reducer = combineReducers({
login: loginReducer,
sites: siteReducer,
})
export default configureStore({
reducer
});
what I want to do is to get the user id from the logIn Slice to the Sites Slice.
something like store.getState().logIn.user.id
I am having trouble with this );
You should never do any side effect within a reducer - this includes both accessing an external variable like state (as suggested by Muhammad Yaqoob in his answer) as well as executing a axios.post like you are trying there. (Also, that is not only something that should not be done - it will also never work)
This should be done in a middleware or just in a thunk. Since that is a whole new can of worms that goes well beyond the scope of this question, please see the official redux tutorial on this topic: https://redux.js.org/tutorials/essentials/part-5-async-logic
(If you follow this path you will notice that you can access current state values from within middleware, thunks and createAsyncThunk, which should then also answer your question)
The configureStore() function returns the same store object which you can simply get like this:
const store = configureStore({
reducer
})
And then use it, like you normally would.
store.getState()
You might want to export store also.
export default store;
I created my store store/user.js
export const state = () => ({
user: {},
});
export const mutations = {
};
export const actions = {
AUTH ({commit},{email, password}){
console.log('email, password =', email, password)
}
};
export const getters = {};
component:
<template>
<form #submit.prevent="AUTH(model)">
<input type="text" required v-model.lazy = "model.email">
<input type="password" required v-model.lazy = "model.password" >
</template>
<script>
import { mapActions } from 'vuex'
export default {
data() {
return {
model:{
email:" " ,
password:" "
}
}
},
methods: {
...mapActions(['AUTH']),
}
}
In my component , I am trying to execute a vuex action from a module, but I am getting an error, even if this action is defined :
unknown action type: AUTH,
I don't have any idey about problem.
index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user.js'
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
user
}
})
You need to use createNamespacedHelpers:
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('users')
Binding helpers with namespace
Otherwise, the mapping helpers need the full module namespace:
...mapActions([
'users/AUTH'
])
// if you are only using one module in the component
...mapActions('users', [
'AUTH'
])
Nuxt
You're mixing classic and modules mode. When using modules mode, Nuxt creates the store instance from the index.js file. You simply export the state, getters, mutations and actions. State should be exported as a function:
export const state = () => ({
foo: 0,
bar: 1
})
Any file within the store directory will be considered a module and Nuxt will automatically register it as a namespaced module.
- store
-- index.js // the store
-- users.js // module 'users'
-- foo.js // module 'foo'
The users module looks otherwise correct.
Make the following changes to your component:
// template
<form #submit.prevent="submitForm">
// script
methods: {
...mapActions({
auth: 'users/AUTH'
}),
submitForm () {
this.auth(this.model)
}
}
I am trying to make use of Material-UI v1 with stajuan's Redux-Saga Login template shown here. So, I want to merge the export default thing of those two, in other words combine two functions for exporting the default class:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { withStyles, createStyleSheet } from 'material-ui/styles';
// Redux
function select (state) {
return {
data: state
}
}
// Material UI v1
const styleSheet = createStyleSheet(theme => ({
// ...
}));
// Class to be exported
class Login extends Component {
// ...
render () {
// ...
}
}
// H O W T O M E R G E T H O S E ? ? ?
// export default connect(select)(Login);
// export default withStyles(styleSheet)(Login);
The last two commented-out lines of the code above are the statements to be combined in my case.
you need to install npm install recompose or yarn add recompose
and on your export section
export default compose(
withStyles(styles, {
name: 'App',
}),
connect(),
)(AppFrame);
or you can do:
export default withStyles(styles)(connect(select)(Cart));
You will be able to access with the key
this.props.domain
Add below line to export your class
const mapStateToProps = state => {
return { domain : 'yourdomain.com'
}
}
export default withStyles(styles)(connect(mapStateToProps)(Login));