The code in my reducer is as follows:
import {ADD_FILTER, REMOVE_FILTER} from "../../../../actions/types";
const removeFilter = (state, name) => {
return state.filter(f => f.name !== name);
};
export default function addRemoveFilterReducer(state = [], action) {
switch (action.type) {
case ADD_FILTER:
let nState = state;
if(state.some(i => i.name === action.searchFilter.name))
nState = removeFilter(state, action.searchFilter.name);
return [...nState, {name: action.searchFilter.name, values: [action.searchFilter.values]}];
//Call another action
case REMOVE_FILTER:
return removeFilter(state, action.searchFilter.name);
default:
return state;
}
}
I have one component showroom and inside the showroom I have Search component and Content component.
Inside search component I handle filtering and I dispatch an action which is handled with this reducer.
After the filter is added I want to dispatch an action with all filters. How can I do that?
That action would be handled with an reducer where I would return only those cars that match search criteria and display them in the content component.
I hope you understand what I wanted to say.
Is this approach good at all?
You may consider to use redux-thunk for this.
You'll have two separate actions, one for adding filter and the other one for making search. Your addFilterAndMakeSearch thunk will be responsible for calling these actions in order. By this way, you won't be need to dispatch an action from your reducer.
// Thunk
function addFilterAndMakeSearch(searchFilter) {
return dispatch => {
dispatch(addFilter(searchFilter);
dispatch(makeSearch());
}
}
// Action Creator One
function addFilter(searchFilter) {
return {
type: 'ADD_FILTER',
payload: searchFilter,
};
}
// Action Creator Two
function makeSearch() {
return {
type: 'MAKE_SEARCH',
};
}
In order to make this work, you need to use addFilterAndMakeSearch as your onSubmit handler.
Calling an action is most probably side effect operation. As reducers should follow functional programming principles, I would argue it shouldn't trigger any actions.
To solve your use case, you can still fire two actions from place that triggered change in your reducer. We are doing that sometimes in our app. For example your component can trigger two actions or action can fire two Redux store updates.
Related
I've got a weird problem where I dispatch an action correctly, but the reducer doesn't get called. I'm using redux-thunk and everything is set up exactly the same how it's set up in one of my other projects. The action type matches, but nothing happens.
Also, if I put a console.log on the default case of the reducer, it gets logged 4 times every time I refresh the page, so I suppose the reducer gets loaded correctly.
export const submitForm = names => {
console.log(names); //This logs what it's supposed to log every time
// I press the submit button
return dispatch => {
dispatch({
type: "SUBMIT_FORM",
names
});
};
};
export default function form(state = {}, action) {
switch (action.type) {
case "SUBMIT_FORM":
console.log(action.names);
return Object.assign({}, state, {
names: action.names
});
default:
return state;
}
}
//My component is connected like this:
const mapStateToProps = state => ({});
const mapDispatchToProps = dispatch => ({
formActions: bindActionCreators(formActions, dispatch)
});
export default connect(mapStateToProps, mapDispatchToProps)(Home);
It seems a little strange to me that an action creator returns a function. Action creatorsnormally return an action, that is, an object. Async action creators returns functions. So, you can try refactor your action creator that will return a objetct, and then you pass the action creator to the dispatch as an argument.
export const submitForm = names => {
console.log(names); //This logs what it's supposed to log every time
// I press the submit button
return {
type: "SUBMIT_FORM",
names
}
};
dispatch(submitForm(names));
Besides that, according to react-redux.js.org/mapdispatch to use bindActionCreators in mapDispatchToProps, mapDispatchToProps does not return an object, but does return bindActionCreators.
const mapDispatchToProps = dispatch =>
bindActionCreators(formActions, dispatch);
another point, if you need to pass an argument to your action creator I would do like this:
const mapDispatchToProps = dispatch => ({
submitForm: (names) => dispatch(submitForm(names))
});
Unfortunately, I can't see all of your code. I hope I helped you with something.
I want to save the current state of a specific reducer into session storage without explicitly calling it on every action.
const { handleActions } = require("redux-actions");
// How do i make this logic run on any state change without adding it to every action?
const onStateChange = (state) => sessionStorage.save(keys.myReducer, state);
const myReducer = handleActions
({
[Actions.action1]: (state, action) => (
update(state, {
prop1: {$set: action.payload},
})
),
[Actions.action2]: (state, action) => (
update(state, {
prop2: {$set: action.payload},
})
)
},
//****************INITIAL STATE***********************
{
prop1: [],
prop2: []
}
);
Is there a way to catch the state change event for a specific reducer?
I know of store.subscribe, but that's for the entire store, i'm talking about listening to a specific reducer change
Thanks
Unfortunately you can't watch specific parts of your store because your store is actually an only reducer (combineReducers return a single big reducer). That said you can manually check if your reducer has changed by doing state part comparison.
let previousMyReducer = store.getState().myReducer;
store.subscribe(() => {
const { myReducer } = store.getState();
if (previousMyReducer !== myReducer) {
// store myReducer to sessionStorage here
previousMyReducer = myReducer;
}
})
You can use redux-saga to listen for specific actions and persist the store (and do your other logice) when they are fired. This seems like a good fit for your needs.
It's not exactly tied to specific store changes, but rather to a set of actions. However, I think this model (listening for actions, as opposed to state changes) is better suited to the redux design.
The nice thing is you don't have to change any code outside of the saga. I've used this one for autosaving form data and it worked great.
Here's an example of setting up a saga to persist on a few redux-form actions; note that it's not limited to persisting - you can do whatever you want during a saga.
function* autosaveSaga(apiClient) {
yield throttle(500,
reduxFormActionTypes.CHANGE,
saveFormValues,
apiClient);
yield takeLatest(
reduxFormActionTypes.ARRAY_PUSH,
saveFormValues,
apiClient);
yield takeLatest(
reduxFormActionTypes.ARRAY_POP,
saveFormValues,
apiClient);
}
If you want to listen to all action types, or do custom filtering of which actions fire your persistence code, you can pass a pattern, or a matching function to takeLatest:
yield takeLatest(
'*',
saveFormValues,
apiClient);
More on what you can pass to take, takeLatest etc can be found in the docs for take.
Supposing I have an update comment action. When a user updates comment after getting a successful result from Promise I should close comment editor. This is my sample code from my project:
export const updateComment = (comment,callBack/* ? */) => {
return (dispatch, getState){
api.updateComment({...comment}).then((result) => {
/* Do something */
callback() /* ? */
})
}
}
In react component I use action like the following code:
handleUpdateComment(){
dispatch(actions.updateComment(this.state.comment,this.closeCommentEitor)
}
It works well but I think is not a good pattern to close comment editor. I'm looking a correct pattern to close editor without passing callBack like I did if any.
When you are using redux-thunk, you can dispatch an action from another action.
What you can do is that, commentEditor have a state which you store in redux and based on that state open or close the commentEditor
export const updateComment = (comment, comment_id) => {
return (dispatch, getState){
api.updateComment({...comment}).then((result) => {
/* Do something */
dispatch({type: 'CLOSE_COMMENT_EDITOR', id: comment_id})
})
}
}
After this in a reducer on this action change the state of redux store, something like
import update from 'immutability-helper'
var initialState = [{commentId: '1', commentEditorOpenStatus: false}, {commentId: '2', commentEditorOpenStatus: false}]
const reducer = (state = initialState, action) => {
switch(action.type) {
'CLOSE_COMMENT_EDITOR':
const idx = state.findIndex(obj => obj.commentId == action.id);
return update(state, {
[idx]: {
commentEditorOpenStatus: {
$set: false
}
}
})
// Other action handlers here
default: return state
}
}
The only thing that updates your application's state is your reducers.
The reducer should be responsible to update the state of your application and not your action (you are now passing getState).
I suggest you to look at redux-promise-middleware
The middleware enables optimistic updates and dispatches pending, fulfilled and rejected actions, which can be intercepted by the reducer.
I have a List of Users which I want to filter during the user Types in letters in a Textfield.
In my Child component which contains the Input field I pass the input Up via props:
onEnter(event){
console.log("ENTER")
// console.log(event.target.value)
this.props.filterEmployee(event.target.value);
}
In my Container Component I take the value
filterEmployee(val){
console.log(val)
// const allUser = this.props.allUser.allUserData;
allUser .forEach(function(user){
if(user.userNameLast.indexOf(val) != -1){
console.log(user) //works
}
});
}
The allUser is an array of data connected from my Redux-store to the Container Component.
This data are also used to display the list of Users initialzied on componentWillMount.
render() {
console.log("administration")
console.log(this.props)
const allUser = this.props.allUser.allUserData;
return (
<div id="employeeAdministration">
<h1>Mitarbeiter Verwaltung</h1>
<EmployeeAdministrationFilterList
filterEmployee={this.filterEmployee.bind(this)}
/>
{/* suchfeld - Name, Department mit checkbox*/}
<ul>
{allUser.length != 0 ? allUser.map(function (item, i) {
console.log(item)
return <li key={i}>
<UserCard
userData={item}
displayPersonalInfo={true}
showRequestDates={false}
showChangePassword={false}
/>
</li>
})
: ""
}
</ul>
</div>
)
}
}
The problem now is, that I don´t know how to tell the <UserCard /> that the data has changed. How can I pass the data from the function to the render() function?
What would be the way to go here?
EDIT:
I have also tried to go the way via the reducer, but so far it didn´t worked.
filterEmployee(val){
console.log(val)
const {dispatch} = this.props;
dispatch(filterAllUser(val));
}
And the Reducer (which is not working)
function allUser(state = {allUserData: []}, action){
switch (action.type){
case 'REQUEST_ALL_USER':
return Object.assign({}, state, {
isFetching: true
});
case 'RECEIVE_ALL_USER':
return Object.assign({}, state, {
isFetching: false,
allUserData: action.items
});
case 'FILTER_ALL_USER':
return Object.assign({}, state, {
allUserData: state.allUserData.filter(user => user.userNameLast !== action.filter )
});
default:
return state
}
}
And here is the Code how the store is connected to the component
EmployeeAdministration.PropTypes = {
allUserData: PropTypes.array
};
const mapStateToProp = state => ({
allUser: state.allUser
});
export default connect(mapStateToProp)(EmployeeAdministration)
When trying this, the result is Console output of state object
This example should be able to demonstrate a basic workflow: JSFiddle.
Basically, Redux has a one-way-dataflow. The data (here is Users in the store) is flowed from the root component to the sub-components.
Whenever you want to change the value of Users inside whichever component, you create an Action and dispatch the action to some corresponding reducer. The reducer updates the store and pass it from top to bottom.
For example, you want to filter all users whose name contains "Jo":
Action creator
Pass the Action creators into the components. An action is a plain object with format like {type: "FILTER_ALL_USERS", query: "Jo"}. Here the passing is line 73:
<Users users={this.props.users} actions={this.props.actions}></Users>
Inside the component Users, we can call this.props.actions.filter() to create an action.
Dispatch the action created
This action is automatically dispatched by redux because we have bindActionCreators in Line 93:
// Map the action creator and dispatcher
const mapDispatchToProps = (dispatch) => {
return {
actions: Redux.bindActionCreators(userActions, dispatch),
dispatch
}
}
Reducer to handle the action
All reducers will be informed about this action, but a particular one will handle it (based on its type), Line 20:
case 'FILTER_ALL_USERS':
return allUsers.filter(user => user.name.toLowerCase().indexOf(action.query.toLowerCase()) >= 0)
Re-render
The reducer will return a brand-new object as the new store, which will be passed by Redux from the root of the component. All render functions in sub-components will be called.
What is the consensus on an action affecting multiple parts of the state tree in Redux?
For example:
const ADD_POST = 'POST/ADD';
function postsReducer(state = initialState, action = {}) {
// switch ...
case ADD_POST:
return {
...state,
...action.result.post
}
}
function anotherReducer(state = initialState, action = {}) {
// switch ...
case ADD_POST:
return {
...state,
post_id: action.result.post.id
}
}
I'm seeking advice on:
Actions affecting multiple parts of the redux store/state
Yes, absolutely. It’s the whole reason why actions exist: to separate what happened from the component’s point of view from what actually happens in terms of state change.
Yes, it's ok. If that's what you want to happen.