Good day,
The beginnings of learning redux can be confusing
The steps in this code:
Create reducer for increment
Create Store
If user takes onClick action, function increment dispatch previously mentioned reducer
In this same component display data
Render component inside Provider with store
In this case need is saving the state with data to LocalStorage.
What should I do for save counter_state to local-storage?
// reducer
function counter(state=0, action) {
console.log('counter', action)
switch(action.type) {
case 'INCREMENT':
return state + 1;
return state;
}
}
///create store
const store = createStore(counter);
// React Component
class Counter extends React.Component {
increment() {
this.props.dispatch({
type: 'INCREMENT'
});
}
render() {
return (
<div>
{this.props.state}
<div>
<button onClick={this.increment.bind(this)}>+</button>
</div>
</div>
)
}
}
const mapStateToProps = function (state) {
return {state};
}
const CounterApp = connect(mapStateToProps)(Counter);
class Test5 extends React.Component {
render() {
return (
<Provider store={store}>
<CounterApp />
</Provider>
)
}
}
export default Test5;
localStorage has a pretty simple interface. I'd recommend looking up its API on mdn. Since you're not using any of the persistence libraries you're importing, might as well try it directly for learning purposes:
Write the value of state.counter to localStorage in your mapStateToProps
When you do createStore you can pass a second argument with preloadedState. So before you do that, read from localStorage to get the saved value of your counter.
Some remarks:
state in the reducer is usually an object with properties. It might work with a simple number, but if you want to learn redux you'll want to use an object here.
I'd recommend to move on to the more complicated libraries like redux-persist once you feel comfortable with the basics of redux. It's too much abstraction at once for getting a simple counter to work.
Related
I have the following reducer:
const selectEntityReducer = function(entityState, action) {
const selectedEntity = entityState.allEntities.filter(e => (e.id == action.id))[0]
const newStateForSelectEntity = updateObject(entityState, {selectedEntity: selectedEntity});
return newStateForSelectEntity;
};
entityState shape looks like {entities:[{id:1}, {id:2}], selectedEntity:{id:2}}.
I have a React component that is connected to the store like this:
function mapStateToProps(state) {
return {
selectEntity: state.entities.selectEntity
};
}
However, it never re-renders.
If I say selectEntities: state.entities in mapStateToProps, then the component re-renders. But I'm only interested in the selected entity for this component.
I'm aware that enforcing immutability down the entire state tree is required for redux to function properly. But I thought that is what I was doing in selectEntityReducer.
Any extra context can be seen in the full redux code in this gist.
Its happens because you filter old piece of state.
You must copy your entities:
const newEntities = entityState.allEntities.slice();
const newSelectedentities = newEntities.filter.........
Copy piece of ctate if it array and then process it.
Gee, I feel foolish about this, but I have read every part of: http://redux.js.org/ (done the egghead tutorials, and read 4 times the FAQ at: http://redux.js.org/docs/faq/ImmutableData.html
What I did was stub one of my reducers, to always return state, and that is the only reducer being called (checked with breakpoints). Even so, my subscribe event is being called every time the reducer returns state. What Do I not understand? (Action.SetServerStats is being called at a 1Hz rate, and the subscribe is also being called at a 1Hz Rate
BTW the Chrome Redux Extension says thats states are equal, and the React Extension for Chrome with Trace React Updates, is not showing any updates.
I will be glad to remove the question, when someone clues me in. But right now, what I see each each of the reducers being called at 1Hz, and all of them returning the slice of the store that they got (state).
So do I not understand subscribe, and that it returns every time even when the store tree does not get modified (and it is up to react-redux to do shallow compare to figure out what changed if any?)
create store & subscribe
let store = createStore(reducer, initialState, composeWithDevTools(applyMiddleware(thunk)))
store.subscribe(() => console.log("current store: ", JSON.stringify(store.getState(), null, 4)))
reducers.js
import A from './actionTypes'
import { combineReducers } from 'redux'
export const GLVersion = (state = '', action) => {
switch (action.type) {
case A.SetGLVersion:
return action.payload
default:
return state
}
}
export const ServerConfig = (state = {}, action) => {
switch (action.type) {
case A.SetServerConfig: {
let { ServerPort, UserID, PortNumber, WWWUrl, SourcePath, FMEPath } = action.payload
let p = { ServerPort, UserID, PortNumber, WWWUrl, SourcePath, FMEPath }
return p
}
default:
return state
}
}
export const ServerStats = (state = {}, action) => {
switch (action.type) {
case A.SetServerStats:
return state
// let { WatsonInstalled, WatsonRunning, FMERunning, JobsDirSize } = action.payload
// let s = { WatsonInstalled, WatsonRunning, FMERunning, JobsDirSize }
// return s
default:
return state
}
}
export default combineReducers({ GLVersion, ServerConfig, ServerStats })
Correct. Redux will execute all subscription callbacks every time an action is dispatched, even if the state is not updated in any way. It is up to the subscription callbacks to then do something meaningful, such as calling getState() and checking to see if some specific part of the state has changed.
React-Redux is an example of that. Each instance of a connected component class is a separate subscriber to the store. Every time an action is dispatched, all of the wrapper components generated by connect will first check to see if the root state value has changed, and if so, run the mapStateToProps functions they were given to see if the output of mapState has changed at all. If that mapState output changes, then the wrapper component will re-render your "real" component.
You might want to read my blog post Practical Redux, Part 6: Connected Lists, Forms, and Performance, which discusses several important aspects related to Redux performance. My new post Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent also goes into detail on how several parts of Redux actually work.
Basic Problem
I've got a Redux store with the following data:
foo: {
currentId: 1,
things: [{id: 1}, {id: 2}),
}
I'd like to make a utility method somewhere (eg. on a Foo singleton object) such that any module in my code can do:
import Foo from 'foo';
foo.getCurrentFoo(); // returns foo.thins[foo.currentId];
but I'm having trouble figuring out where to put it.
Failed Attempt
My initial attempt was to create a Foo component singleton:
// Foo.js
class FooBase extends React.Component {
getCurrentFoo() {
return this.state.foo.things[this.state.foo.currentId];
}
}
const Foor = connect((state) => state.foo)(FooBase);
export default new FooWrapper();
But that doesn't work. Redux complaieds about the property store not existing (when I did new FooWrapper()). That makes sense, because my component isn't inside a <Provider />. However, I just want a stand-alone utility class/object, not something actually in the DOM, which rules out <Provider/>.
How can I make a method like the one described above, that actually works, without involving <Provider /> ... and where do I put it?
The nice thing about the react-redux helpers is that they allow you to use connect() and <Provider /> to automatically pass the store to child components via React's context. However, that doesn't necessarily mean that you have to use these helpers, especially in areas of a codebase that don't use React.
So here lies the problem: connect() and <Provider /> help us by giving our React components access to a singleton instance of a store, but how can we access this store somewhere where connect() and <Provider /> can't be used?
I think the easiest solution here is to create a singleton class that holds on to the store, so any non-React module can still use the store.
So let's say you're creating your store like this:
init.js
import {createStore} from 'redux';
const initialState = {
currentId: 1,
things: ['foo', 'bar']
};
const reducer = (state = initialState, action) => {
if (action.type === 'SET_CURRENT_ID') {
return Object.assign({}, state, {
currentId: action.id
});
}
return state;
};
const store = createStore(reducer);
This store takes an action of type SET_CURRENT_ID which simply returns a new state with the currentId property changed to whatever was handed to it. You could then get the current "thing" by doing something like store.getState().things[store.getState().currentId]. So let's create a Singleton class that can hold on to the store and provide a wrapper around this functionality.
store.js
class Store {
constructor() {
this._store = undefined;
}
setStore(store) {
this._store = store;
}
getCurrentThing() {
if (this._store) {
const {things, currentId} = this._store.getState();
return things[currentId];
}
}
setCurrentThing(id) {
if (this._store) {
const action = {
type: 'SET_CURRENT_ID',
id
};
this._store.dispatch(action);
}
}
}
export let singletonStore = new Store();
This class creates an instance the first time it is used, and uses that instance every subsequent time. So when you originally create your store, simply import this class and call setStore().
init.js
import {singletonStore} from './store';
singletonStore.setStore(store);
Then, every subsequent file where singletonStore is used will have the same state.
test.js
import {singletonStore} from './store';
console.log(singletonStore.getCurrentThing()); // 'bar'
singletonStore.setCurrentThing(0);
console.log(singletonStore.getCurrentThing()); // 'foo'
This should work just fine for your need to use your store in modules that don't have the benefit of being passed the store magically with connect() and <Provider />.
All:
I am pretty new to Redux, when I follow its Reddit API example, there is one code snippet confuse me so much:
In AsyncApp.js, there is:
componentDidMount() {
const { dispatch, selectedSubreddit } = this.props
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
I wonder where the dispatch and selectedSubreddit get bind to this.props?
Thanks
That example is using the connect() function from react-redux to inject certain parts of the Redux state and the store's dispatch() function as props in that component. See the 'Usage With React' part of the Redux docs for more information.
For example:
App.js:
export class App extends Component {
//...
}
function mapStateToProps(state) {
const { selectedReddit, postsByReddit } = state
const {
isFetching,
lastUpdated,
items: posts
} = postsByReddit[selectedReddit] || {
isFetching: true,
items: []
}
return {
selectedReddit,
posts,
isFetching,
lastUpdated
}
}
export default connect(mapStateToProps)(App)
The connect() function here is taking the mapStateToProps() function above to inject the appropriate parts of the Redux state as props in the <App /> component. The keys of the object returned by mapStateToProps() correspond to the names of the props injected, and the corresponding values are the values of those injected props.
connect() can also take a second argument, matchDispatchToProps(), which can be used to inject specific action dispatch functions as props in your component. Whether or not you supply any arguments to connect(), it will inject your Redux store's dispatch() function as a prop called dispatch.
These connected container components receive state updates from the store, so when your Redux state changes, the connected container components will receive new props accordingly.
I'm trying to build keyboard shortcut support into my React/Redux app in an idiomatic React/Redux way. The way I am planning to do this is to have the following action creator and associated action:
registerShortcut(keyCode, actionCreatorFuncReference)
The reducer would then update a registeredShortcuts object in the redux store with a mapping of keyCodes to actionCreatorFuncReferences. Then my root component would listen for keyup and see if there is an associated keyCode registered and if so, then dispatch the mapped action via the action creator function reference.
However, this would be the first time I am storing function references in my Redux store. To date, I've only had objects with keys with vanilla values (strings, ints, etc).
The Redux docs says:
You should do your best to keep the state serializable. Don’t put anything inside it that you can’t easily turn into JSON.
Does this suggest it's a bad idea to store such function references in my Redux store? If so, what is a better way to accomplish what I'm trying to do in React/Redux?
An alternative approach is just to store the mapping of keyCodes and function references in the root react component itself, but that didn't feel very Redux-like since now the application state is not in the Redux store.
No, you should not store function references in the redux store. They are not serializable, and as you mentioned state should be serializable at all times. The most redux-friendly approach I can think of is just to keep the map of hotkeys to their actionCreatorFuncNames.
TL;DR: You don't. The store state must be serializable at all times (as Nathan answered).
The Redux way is via enhancers, or the Redux-Observable way via dependencies.
NL;PR: Based on the Redux docs example, what you want is to pass the reference in your action(1), ignore it your reducer(2) and use it in your enhancer(3):
//... in your action:
const data={val:1}, ref=()=>{};
const action = {type:'ACTION_WITH_REF', data, ref}; //(1)
//... in your reducer:
case 'ACTION_WITH_REF':
return {...state, data: action.data}; //(2)
//... and in your enhancer:
import { createStore, applyMiddleware } from 'redux';
import reducers from './reducers';
export const myRefStore= {};
function refHandler({ getState }) {
return next => action => {
switch(action.type){
// this can be done more elegantly with a redux-observable
case 'ACTION_WITH_REF':
myRefStore.aRef = action.ref; // (3)
break;
}
// be sure to maintain the chain of the store
const returnValue = next(action);
// otherwise, your midddeware will break the store
return returnValue;
};
}
const store = createStore(
reducers,
initialState,
applyMiddleware(refHandler)
);
Note: As far as there are no side-effects in your enhancers, you are good to go. Be aware that you could have obtained the refs directly in the reducers, but such an approach keeps the reference at the reducer-level and misses the point of combineReducers(). With an enhancer, you keep them all in one place(myRefStore).
One final observation is that a redux store is not an any-data store but a state store, thus why we need to handle functions and other non-state related stuff in enhancers. You can leverage the enhancer backbone to Redux-Observable and inject myRefStore via dependencies.
I'm new to redux, but the way I see it, you could pass the key code and an action type.
Then a reducer could be listening for that action type and make changes accordingly.
Here is an example using the library Mousetrap:
// On your Container
function registerShortcut(element, dispatch, keyCode, actionType) {
Mousetrap(element).bind(keyCode, function(e) {
dispatch({
type: actionType,
payload: {
keyCode: keyCode,
event: e
}
});
});
});
mapDispatchToProps = function(dispatch) {
return {
onMount: function(element) {
registerShortcut(element, dispatch, ['command+f', 'ctrl+f'], 'OPEN_SEARCH');
},
onUnmount: function(element) {
Mousetrap(element).unbind(['command+f', 'ctrl+f']);
}
};
};
// On your Component
componentDidMount() {
onMount(ReactDOM.findDOMNode(this));
};
componentWillUnmount() {
onUnmount(ReactDOM.findDOMNode(this));
};
// On your reducer
function reducer(oldState, action) {
if (action.type == 'OPEN_SEARCH') {
//... make changes ...//
return newState;
}
return oldState;
};
This way, keyboard shortcuts will dispatch an action. The reducer will make the changes necessary to the state. And finally, the application can re-render.