I have a Colors.js file which contains some vars such as:
const textColor = 'white';
const primaryColor = 'blue';
I then import this file in a components styling and use it as such:
import Colors from './Colors';
const styles = {
component: { color: Colors.textColor }
}
Now I would like to connect this Colors.js file to my redux store; so I can theme my app. For example, wrapping my Colors.js file in a connect function, however, this doesn't work as the Colors.js is called way before the store is initialized.
I also tried getState() and importing the store manually, but I did not receive any props.
Is there a different approach I could take?
Thank you.
Make the colors.js into a reducer and set it as such. In the reducer, set your initialState to default colors you need, and also export that initialState object, so you can import it in other files. Now you can import that initialState and use it anywhere you want in the app, which will be constant, and also you will have that same colors object in redux store, which you can connect to any component you have and use and manipulate it too, with redux actions.
Related
Is there any way of getting the state value without using useSelector with using it in React Component
import { superAdmin, zoneManager } from './navigations'
import { useSelector } from 'react-redux'
const access = useSelector(state => state.access)
return access
The Error I get is:
Invalid hook call. Hooks can only be called inside of the body of a
function component.
Which I get it but is there any other way of getting the store value without the use of useSelector
Thank You
To make the store accessible anywhere (for whatever reason), you first need to export the store from the file where you are making it:
// fileThatMakesStore.js
export const store = configureStore();
Then, back in your file, you can simply import the store and getState().
import { store } from 'fileThatMakesStore.js';
const someValue = store.getState().something;
const someValue = store.getState().someReducer.something; // If you have multiple reducers
Good to know: The store is just a simple JS object with three functions: subscribe(), dispatch(), getState().
You can use getState of store, like this:
const access = store.getState().access
This is one of the strangest things I have ever seen. It makes absolutely no sense to me. The short version is I have a Redux action creator function. If I import this function into this one particular component file, it makes every function imported from its file undefined.
So, let's start with the file filterInputModal.actions.js. This contains my Redux action functions, created using redux-starter-kit:
export const showAddCategoryModal = createAction('showAddCategoryModal');
That is the function I've been working with. Now, this function has long since been imported into my ManageVideoFilters.js component:
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { showAddCategoryModal } from 'store/filterInputModal/filterInputModal.actions';
const ManageVideoFilters = (props) => {
/* Component logic */
};
/* PropTypes and mapStateToProps */
const mapDispatchToProps = (dispatch) => bindActionCreators({
showAddCategoryModal: () => showAddCategoryModal() // Done this way to avoid passing in a payload, since certain default event payloads cause Redux to print console errors
});
export default connect(mapStateToProps, mapDispatchToProps)(ManageVideoFilters);
So far so good. Before we go and break everything, let's take a look at my filterInputModal.reducer.js Redux reducer, also created using Redux Starter Kit:
import { createReducer } from 'redux-starter-kit';
import { showAddCategoryModal } from './filterInputModal.actions';
const initialState = {}; // The initial state for the reducer goes here
const handleShowAddCategoryModal = (state) => {
/* handle updating the state */
return state;
};
const actionMap = {
[showAddCategoryModal]: handleShowAddCategoryModal
};
export default createReducer(initialState, actionMap);
The action map uses the action creator functions toString() as the key, and then I provide my own functions to handle updating the state. Again, at this point, everything is perfect. We will come back to the reducer in a sec, first let's break things.
Now we're going to my VideFileEdit.js component. If we add the following line to this component, everything breaks:
import { showAddCategoryModal } from 'store/filterInputModal/filterInputModal.actions';
So, how does it break?
The import of the showAddCategoryModal function in filterInputModal.reducer.js now is undefined.
Because the reducer is using the functions as the keys to handle actions, the reducer is no longer able to handle the action properly and update the state.
It gets weirder though. Here are some of the weird behaviors I'm seeing.
If I import this action into any other component, everything is fine. The import in the reducer is unchanged.
The import of the function in both ManageVideoFilters.js and VideoFileEdit.js is fine.
So, what can I try next? This is really strange and doesn't make any sense to me. I've never seen this before.
As the commenter said, the problem was recursive imports. My filterInputModal.reducer.js exported some constants, which were imported into my filterInputModal.actions.js. The actions from filterInputModal.actions.js were then imported into filterInputModal.reducer.js. Hence the recursive import.
I moved the constants into a new file, filterInputModal.constants.js, and viola, problem solved.
i have a file inside action directory which is root.js.
Root.js will compile all the others action inside, and i bind it with bindActionCreators
export const all = (store) => {
AUTH: bindActionCreators(AUTH.actions, store.dispatch),
....: .....
}
From what i learned, bindActionCreators is for the purpose of auto dispatching the action.
If that is the case, then how do i access it from smart component?
I see things like dispatch(action). But since now i already bind it globally, i dont think that i would need to specify dispatch anymore. How do i do it, or is there any part that i misunderstood?
Thank you
bindActionCreators - will create an object of actions each wrapped with the dispatch.
It's good for passing them as refs to non-connected components that should not know anything about redux or dispatch.
Quote from the DOCS:
The only use case for bindActionCreators is when you want to pass some
action creators down to a component that isn't aware of Redux, and you
don't want to pass dispatch or the Redux store to it.
So if you want that connected component to pass action creators to a dumb component, you can set an object via bindActionCreators and pass it with props to the dumb component.
Example:
const myActionCreators = bindActionCreators(Auth.myActions, dispatch)
<DumbComponent {...myActionCreators} />
The recommended approach is to have each connected component file import the action creators it needs, and use the "object shorthand" supported by connect:
import {addTodo, toggleTodo} from "./todoActions";
const actions = {addTodo, toggleTodo};
export default connect(null, actions)(TodoList);
// each TodoList instance now has this.props.addTodo and
// this.props.toggleTodo, which will dispatch actions when called.
I have three reducers
Home
Listing
Detail
I have one combine reducer for above three. But I want to divide project structure into multiple reducers. Is it a good practice to have multiple combine reducer in the project?
Yes, you can have multiple combineReducers in your App, You can at the top level combine reducers Home , Listing and Detail and even split each individual reducers into multiple and combine them into one.
According to the Redux docs:
You may call combineReducers at any level of the reducer hierarchy.
It doesn't have to happen at the top. In fact you may use it again to
split the child reducers that get too complicated into independent
grandchildren, and so on.
Some more description about combineReducer:
As your app grows more complex, you'll want to split your reducing
function into separate functions, each managing independent parts of
the state.
The combineReducers helper function turns an object whose values are
different reducing functions into a single reducing function you can
pass to createStore.
The resulting reducer calls every child reducer, and gathers their
results into a single state object. The shape of the state object
matches the keys of the passed reducers.
Yes, you can combine multi reducer and use it as one store,
Firstly, you should import all reducers in a new file. then, combine all reducers with combine-reducer technics,like this:
import { combineReducers } from 'redux';
import {Home,Listing,Detail} from '*/**';
const rootReducer = combineReducers({
Home,
Listing,
Detail
})
export default rootReducer
Secondly, import the new file in Store file and create your store with combined-reducer
import { createStore } from 'redux'
import rootReducer from '*/combinestores'
const store = createStore(rootReducer)
console.log(store.getState())optinal
export default store
In the end, in your container you can use your store like this :
(but before it, you should be sure send store as props to Containers)
const mapStateToProps = state => {
return{
*name of your var* : state.Home.*,
*name of your var* : state.Detail.* /*then you can use this var as props anywhere you need*/
}
}
const mapDispatchToProps = dispatch => {
return{
*name of your var* : () => dispatch ( *name of your func*()),
*name of your var* : () => dispatch ( *name of your func*())
}
}
export default connect(mapStateToProps,mapDispatchToProps)(name of func);
I am using react-redux and I am having a issue where I loose my redux state when the page is refreshed.
Now before I go further on that this is the scenario, which I may be messing up.
Question one: Can I connect to more than one layout?
I have a dashboard and a "app" layout. Both have separate layouts. I connect both in the same manner:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../actions/actionCreators';
function mapStateToProps(state) {
return {
profile: state.profile,
child: state.child,
}
}
function mapDispachToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
const LayoutApp = connect(mapStateToProps, mapDispachToProps) (DashboardLayout);
export default LayoutApp;
The dashboard connects just fine. I am able to hit reducers when i need to and update the store, however the dashboard links to the app for certain parts of data manipulation for you to play with. When it links I get the data in props as expected, however as soon as any page refreshes within the app layouts I loose the props being sent by maptoprops.
I have tried to combine into one master layout however that seems to have the same affect. I have also tried to save to state immediately when i first receive data but that seems to be lost as well upon refresh which makes me think it is resetting it.
Summary:
- DashboardLayout (connects to redux)
- AppLayout (connects to redux) however after a page refresh it looses props to the Applayout and needed data is gone.
Get to know redux-persist
https://github.com/rt2zz/redux-persist
You can install it using
npm i --save redux-persist
persistStore is the function that allows you to persist store.
import {persistStore} from 'redux-persist'
and autoRehydrate is the action that is performed whenever the state needs to be rehydrated
import {autoRehydrate} from 'redux-persist'
following is the structure that may be useful.
import {compose, applyMiddleware, createStore} from 'redux'
import {persistStore, autoRehydrate} from 'redux-persist'
// add `autoRehydrate` as an enhancer to your store (note: `autoRehydrate` is not a middleware)
const store = createStore(
reducer,
undefined,
compose(
applyMiddleware(...),
autoRehydrate()
)
)
// begin periodically persisting the store
persistStore(store)
and then for your reducer
import {REHYDRATE} from 'redux-persist/constants'
//...
case REHYDRATE:
var incoming = action.payload.myReducer
if (incoming) return {...state, ...incoming, specialKey:
processSpecial(incoming.specialKey)}
return state
following are the methods which can be used to work out of redux-persist
persistor.pause() - pauses redux persist
persistor.resume() - resumes redux persist
persistor.purge() - deletes all persisted data
persistor.rehydrate() - calls reducer to rehydrate store
types of storage redux persist allows
// sessionStorage
import { persistStore } from 'redux-persist'
import { asyncSessionStorage } from 'redux-persist/storages'
persistStore(store, {storage: asyncSessionStorage})
// react-native
import {AsyncStorage} from 'react-native'
persistStore(store, {storage: AsyncStorage})
// web with recommended localForage
import localForage from 'localforage'
persistStore(store, {storage: localForage})
it is the most basic use redux-persist hope it helps.
You can connect to as many components as you want to same store properties - that's not issue. Just to note here, connect is listening for changes so when action changes store it would cause all components listening to changed store parts to re-render (or at least do computation and then compare shadowdom).
Store is stored only when running application - after page close/resfresh it's cleared.
There is good thread about persistence of store (eg. to local storage): Where to write to localStorage in a Redux app?
As mentioned in the previous answers storing the state in local storage is one possible solution however some other possible solutions are
Make sure your links use the Push API ie <Link to={"route"}/> (react-router) this will ensure the store is not removed between route changes
Set your initial state when you create your store
const rootReducer = combineReducers({
todos: todos,
visibilityFilter: visibilityFilter
});
const initialState = {
todos: [{id:123, text:'hello', completed: false}]
};
const store = createStore(
rootReducer,
initialState
);
This will hydrate your reduce store when it is initialized
There are some other libs/frameworks/boilerplates that you could use that also implement server rendering with will render the HTML to the page on the initial render and then load React once the DOM has loaded some of this libs are
Next.js,Electroide,react-redux-universal-hot-example
Going down this route will add significant complexity to your app so you should weigh up the pros and cons before embarking down this route
I had this problem, so as somebody mentioned here, i used the localStorage.
In my reducer, i did like this:
const exampleKey = "_exampleKey"
const INITIAL_STATE = {
example: JSON.parse(localStorage.getItem(exampleKey))
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SET_TRUE':
localStorage.setItem( exampleKey, JSON.stringify(true));
return { ...state, example: true };
case 'SET_FALSE':
localStorage.setItem( exampleKey, JSON.stringify(false));
return { ...state, example: false};
default:
return state
}
Pointing INITIAL_VALUES.example to localStorage's item example ensure us to keep the correct value when the page reloads.
Hope it helps somebody.