I tried to use selectbyId helper method of reduxtoolkit without a success can anyone help me with this
Category Slice
export const {
selectAll:selectCatetories,selectById:selectCategoryById
}=categoriesAdapter.getSelectors(state=>state.inventory.categories.categories)
Category Component
const category=useSelector((id)=>selectCategoryById(id))
I am sure there is something wrong with my implementation
To get your category with an Id, you will need to create a custom selector with reselect
import { createSelector } from '#reduxjs/toolkit';
const { selectById } = categoriesAdapter.getSelectors();
export const getCategoriesState = (rootState) => rootState.categories; // I am assuming you have a 'categories' reducer in your redux store.
export const selectCategoryEntity = (id) => {
return createSelector(getCategoriesState, (state) => selectById(state, id));
}
Then, you will be able to use the selector hook in your React component:
const category = useSelector(selectCategoryEntity(id));
You just need to pass the state to the useSelector function.
const category = useSelector((state)=>selectCategoryById(state, id))
Related
In react, if we want to make a calculation which is based on a property set inside the state, we can write the setState as follows:
const [counter, setCounter] = useState(5)
// instead of this:
// setCounter(counter + 1)
// you can do this:
setCounter((previousState)=> previousState + 1)
This is one of the fundamentals in react.
I am having a complex problem in my code, let's say, so I have no idea how to explain it in a simple question, but let's try to hide all that for now and see if it requires posting the details later 😁.
Is there a similar thing in redux toolkit where I can for instance do something as follows:
import { setCounter } from 'features/posSlice'
const MyComponent = () => {
const dispatch = useDispatch()
dispatch(setCounter((previousState) => previousState + 1))
}
Now you might ask, why you don't implement this inside the reducers? so by doing something as follows:
import { createSlice } from '#reduxjs/toolkit'
const posSlice = createSlice({
name: 'pos',
initialState: {
counter: 5
},
reducers: {
incrementBy = (state, { payload }) => {
state.counter = state.counter + payload
}
}
})
I would say, okay… that's a good question, the reason is, not easy to explain because this is not the real example.
Just for now, let's make it simple:
I would like to know if there's a method to get the current state among all these async calls TO get the correct..current..state in order to build upon it. from inside the controllers, not the models.
So, something like this inside the component, not the slice:
import { setCounter } from 'features/posSlice'
const MyComponent = () => {
const dispatch = useDispatch()
dispatch(setCounter((previousState) => previousState + 1))
}
Is it possible?
thanks 🌹
https://github.com/reduxjs/redux-toolkit/issues/3122#issuecomment-1404879054
I'm sorry, but the question is really "why you don't implement this
inside the reducers".
Caclulation of new state should always happen in a reducer - you
should only pass stuff you need for that calculation into the payload,
never the new value. Please give the Redux Style Guide a good read.
That said, you can do this in a thunk, although I would really
recommend against it.
dispatch((dispatch, getState) => {
dispatch(setCounter(getState().yourCounterValue + 1)) })
So simply do it as
import { useDispatch } from 'react-redux'
// inside a component
const dispatch = useDispatch()
dispatch((dispatch, getState)=>{
dispatch(getState().pos.counter + 1)
})
I've been trying to get my Pinia store up and running in Vue 3 and it all has been pretty effortless until I wanted to access some parameters in the url.
I have a store (simplified) like so:
import { defineStore } from 'pinia';
import { useRoute } from 'vue-router';
import { useLocalStorage } from '#vueuse/core';
export const useUserStore = defineStore('user', () => {
const route = useRoute();
const uuid = ref(
useLocalStorage('uuid', route.params.id)
)
return { uuid };
})
Unfortunately, the route remains undefined as if useRoute() is not triggered properly. I've seen that you can add plugins to add the router instance to the pinia store on initialisation, but there's no way I can find to access that this instance in a Setup Store.
Any help would be greatly appreciated
route is not defined when the pinia is initiated.
You need to wait a bit.
One way to do this is to call the function when the component is loaded.
export const useUserStore = defineStore('user', () => {
const route = useRoute();
const id = ref('');
const setId = () => {
id.value = route.params.id as string; // don't need as string if you don't use TypeScript
};
return { id, setId };
});
<script setup lang="ts">
import { useUserStore } from '../stores/user';
const user = useUserStore();
user.setId(); // call the function from pinia, route.params works just fine
</script>
Link Demo
Fairly new to redux, react-redux, and redux toolkit, but not new to React, though I am shaky on hooks. I am attempting to dispatch an action from the click of a button, which will update the store with the clicked button's value. I have searched for how to do this high and low, but now I am suspecting I am thinking about the problem in React, without understanding typical redux patterns, because what I expect to be possible is just not done in the examples I have found. What should I be doing instead? The onclick does seem to capture the selection, but it is not being passed to the action. My goal is to show a dynamic list of buttons from data collected from an axios get call to a list of routes. Once a button is clicked, there should be a separate call to an api for data specific to that clicked button's route. Here is an example of what I currently have set up:
reducersRoutes.js
import { createSlice } from "#reduxjs/toolkit";
import { routesApiCallBegan } from "./createActionRoutes";
const slice = createSlice({
name: "routes",
initialState: {
selected: ''
},
{... some more reducers...}
routeSelected: (routes, action) => {
routes.selected = action.payload;
}
},
});
export default slice.reducer;
const { routeSelected } = slice.actions;
const url = '';
export const loadroutes = () => (dispatch) => {
return dispatch(
routesApiCallBegan({
url,
{...}
selected: routeSelected.type,
})
);
};
createActionRoutes.js
import { createAction } from "#reduxjs/toolkit";
{...some other actions...}
export const routeSelected = createAction("routeSelection");
components/routes.js:
import { useDispatch, useSelector } from "react-redux";
import { loadroutes } from "../store/reducersRoutes";
import { useEffect } from "react";
import { routeSelected } from "../store/createActionRoutes";
import Generic from "./generic";
const Routes = () => {
const dispatch = useDispatch();
const routes = useSelector((state) => state.list);
const selected = useSelector((state) => state.selected);
useEffect(() => {
dispatch(loadroutes());
}, [dispatch]);
const sendRouteSelection = (selection) => {
dispatch(routeSelected(selection))
}
return (
<div>
<h1>Available Information:</h1>
<ul>
{routes.map((route, index) => (
<button key={route[index]} className="routeNav" onClick={() => sendRouteSelection(route[0])}>{route[1]}</button>
))}
</ul>
{selected !== '' ? <Generic /> : <span>Data should go here...</span>}
</div>
);
};
export default Routes;
Would be happy to provide additional code if required, thanks!
ETA: To clarify the problem - when the button is clicked, the action is not dispatched and the value does not appear to be passed to the action, even. I would like the selection value on the button to become the routeSelected state value, and then make an api call using the routeSelected value. For the purpose of this question, just getting the action dispatched would be plenty help!
After writing that last comment, I may actually see a couple potential issues:
First, you're currently defining two different action types named routeSelected:
One is in the routes slice, generated by the key routeSelected
The other is in createActionRoutes.js, generated by the call to createAction("routeSelection").
You're importing the second one into the component and dispatching it. However, that is a different action type string name than the one from the slice - it's just 'routeSelection', whereas the one in the slice file is 'routes/routeSelected'. Because of that, the reducer logic in the slice file will never run in response to that action.
I don't think you want to have that separate createAction() call at all. Do export const { routeSelected } = slice.actions in the slice file, and dispatch that action in the component.
I'm also a little concerned about the loadroutes thunk that you have there. I see that you might have omitted some code from the middle, so I don't know all of what it's doing, but it doesn't look like it's actually dispatching actions when the fetched data is retrieved.
I'd recommend looking into using RTK's createAsyncThunk API to generate and dispatch actions as part of data fetching - see Redux Essentials, Part 5: Async Logic and Data Fetching for examples of that.
I'm giving redux-orm a try and from what I can gather, redux-orm creates an object in store, whose key is the value specified via Model.name, for every registered model.
Well,to create my "entities" reducer, I'm using combineReducers, passing in the reducers for all my entities, like this:
import { combineReducers } from 'redux';
import City from './cityReducer';
import Poi from './pointOfInterestReducer';
const entitiesReducer = combineReducers({
City,
Poi
});
export default entitiesReducer;
And finally this is how I create my rootReducer, also using combineReducers:
The problem is that,I think that, by using combineReducers I'm duplicating the keys in my store like it's shown here:
Do you have any idea how I can avoid this, and have all my models be direct descendants of "entities"?
Something like entities>City and not entities>City>City
Edit: cityReducer.js
import orm from './../../orm/orm';
import * as types from '../../actions/actionTypes';
const loadCities = (state, action) => {
// Create a Redux-ORM session from our entities "tables"
const session = orm.session(state);
// Get a reference to the correct version of model classes for this Session
const { City } = session;
const { cities } = action.payload;
// Clear out any existing models from state so that we can avoid
// conflicts from the new data coming in if data is reloaded
City.all().toModelArray().forEach(city => city.delete());
// Immutably update the session state as we insert items
cities.forEach(city => City.parse(city));
return session.state;
};
const updateCity = (state, payload) => {
const { id, newItemAttributes } = payload;
const session = orm.session(state);
const { City } = session;
if (City.hasId(id)) {
const modelInstance = City.withId(id);
modelInstance.update(newItemAttributes);
}
return session.state;
};
const deleteCity = (state, payload) => {
const { id } = payload;
const session = orm.session(state);
const { City } = session;
if (City.hasId(id)) {
const modelInstance = City.withId(id);
// The session will immutably update its state reference
modelInstance.delete();
}
return session.state;
};
const createCity = (state, payload) => {
const { city } = payload;
const session = orm.session(state);
const { City } = session;
City.parse(city);
return session.state;
};
const citiesReducer = (dbState, action) => {
const session = orm.session(dbState);
switch (action.type) {
case types.LOAD_CITIES_SUCCESS: return loadCities(dbState, action);
case types.CREATE_CITY_SUCCESS: return createCity(dbState, action);
case types.UPDATE_CITY_SUCCESS: return updateCity(dbState, action);
case types.DELETE_CITY_SUCCESS: return deleteCity(dbState, action);
default: return session.state;
}
};
export default citiesReducer;
There's two ways you can use Redux-ORM to define its "tables" structure, and write reducer logic to use those tables. I gave examples of both approaches in my blog posts Practical Redux, Part 1: Redux-ORM Basics, Practical Redux, Part 2: Redux-ORM Concepts and Techniques, and Practical Redux, Part 5: Loading and Displaying Data. (Note that those posts cover use of Redux-ORM 0.8, and version 0.9 has some API changes. I listed those changes in Practical Redux, Part 9.)
The first approach is to write your reducer logic attached to your model classes, and use the Redux-ORM ORM instance to generate a reducer that creates the "tables" and manages them for you. Per the example on the front of the Redux-ORM repo:
import {createReducer} from "redux-orm";
import {orm} from "./models";
const rootReducer = combineReducers({
entities: createReducer(orm)
});
The other approach is to write your own function that creates the initial state, and possibly other functions that handle the updating:
import {orm} from "./models";
const initialState = orm.getEmptyState();
export default function entitiesReducer(state = initialState, action) {
// handle other actions here if desired
return state;
}
In other words, don't define the separate per-model-type "tables" yourself. Let Redux-ORM define those, either automatically in its own reducer, or by using it to generate the initial state for your entities reducer.
I try to implement a async react-select (Select.Async). The problem is, we want to do the fetch in redux-saga. So if a user types something, the fetch-action should be triggered. Saga then fetches the record and saved them to the store. This works so far.
Unfortunately loadOptions has to return a promise or the callback should be called. Since the newly retrieved options get propagated with a changing property, I see no way to use Select.Async together with saga to do the async fetch call. Any suggestions?
<Select.Async
multi={false}
value={this.props.value}
onChange={this.onChange}
loadOptions={(searchTerm) => this.props.options.load(searchTerm)}
/>
I had a hack where i assigned the callback to a class variable and resolve it on componentWillReceiveProps. That way ugly and did not work properly so i look for a better solution.
Thanks
redux-saga is for handling side effects like asynchronously receiving options for react-select. That's why you should leave the async stuff to redux-saga. I have never used react-select but by just looking at the documentation I would solve it this way:
Your component gets very simple. Just get value and options from your redux store. optionsRequested is an action creator for the OPTIONS_REQUESTED action:
const ConnectedSelect = ({ value, options, optionsRequested }) => (
<Select
value={value}
options={options}
onInputChange={optionsRequested}
/>
)
export default connect(store => ({
value: selectors.getValue(store),
options: selectors.getOptions(store),
}), {
optionsRequested: actions.optionsRequested,
})(ConnectedSelect)
A saga definition watches for OPTIONS_REQUESTED action that is trigged by onInputChange, loads the data with given searchTerm from server and dispatches OPTIONS_RECEIVED action to update redux store.
function* watchLoadOptions(searchTerm) {
const options = yield call(api.getOptions, searchTerm)
yield put(optionsReceived(options))
}
In other words: Make your Component as pure as possible and handle all side-effect/async calls in redux-saga
I hope this answer was useful for you.
The main idea is that you are capable to dispatch redux actions using application context from
import React from 'react';
import { connect } from 'react-redux';
import Select from '#components/Control/Form/Skin/Default/Select';
import { reduxGetter, reduxSetter, required as req } from '#helpers/form';
import { companyGetTrucksInit } from "#reduxActions/company";
import AppContext from '#app/AppContext';
const FIELD_NAME = 'truck';
export const getReduxValue = reduxGetter(FIELD_NAME);
export const setReduxValue = reduxSetter(FIELD_NAME);
const SelectCompanyTruck = (props) => {
const {
required,
validate=[]
} = props;
const vRules = [...validate];
if (required)
vRules.push(req);
return (
<AppContext.Consumer>
{({ dispatchAction }) => (
<Select
loadOptions={(inputValue, callback) => {
function handleResponse(response) {
const { data: { items } } = response;
const options = items.map(i => ({ label: i.name, value: i.id }));
callback(options);
}
dispatchAction(companyGetTrucksInit, { resolve: handleResponse, inputValue });
}}
name={FIELD_NAME}
{...props}
/>
)}
</AppContext.Consumer>
);
}
export default SelectCompanyTruck;