I am new to redux world and I am trying to make newspaper app. I am currently working on the search functionality where the user can search for specific newspaper title. The problem is that when I first types eg 'a' the state is ''. And when I type more eg 'b' the state shows that the term is 'a' when it should be 'ab'. I am using redux chrome tools to check this global state.
My Actioncreator is really simple (only returns the term passed to it):
export function search(term) {
return{
type:SEARCH,
term
}
}
Here is the reducer:
const SearchReducer = (state = '', action) => {
switch (action.type) {
case SEARCH:
return action.term;
default:
return state;
}
}
Here is the root reducer:
/*Application state*/
const rootReducer = combineReducers({
weather: WeatherReducer,
filters: FilterReducer,
articles:ArticleReducer,
search: SearchReducer // this should be 'ab', but is 'a'
});
This is really simple setup. Here is how I am communicating with redux.
I have my material-ui textfield (i tried vanilla input field too)
<TextField style={style.searchWidget.search}
floatingLabelText="Search"
value={this.state.term}
onChange={this._onSearchChange}
/>
When the user types something the _onSearchChange func is fired.
_onSearchChange(event) {
this.setState({term: event.target.value});
this.props.search(this.state.term);
}
This will set current state for this search component. Then it will dispatch search action which will update the global state. But does not work correctly. The global state is always 1 step behind.
Any idea?
Edit:
It looks like it is not redux but react. The component state is not updating instantly. Correct term is passed to the actionreducer so it is not redux's fault. I tried to print out this.state.term after setting the state. And it looks like the state is not updating.
Actually it is not related to redux. You'll have to remember while working with react that you can not get the updated state as soon as you update your react state. let's take an example.
getInitialState: function() {
return {
myState: 0
}
},
updateState: function(stateVersion) {
console.log(this.state.myState); //this will print 0
this.setState({ myState: stateVersion });
console.log(this.state.myState); //this will print 0
}
now for the first time if we call updateState function with state version as argument it will print 0 for both the console even if we sent 1 or 2 or any number as argument.
suppose we have sent 2 as argument. then we can get that state in the next call of that function with another arguments.
But react also take care of this.
You may think that if the state is not updated as soon as we update that then how can I work with that. But react is a good boy...
if you call a function to update a state and next you call another function to work with last updated state then you can work with that.
I think I have already answered your question that why not redux state is updated.
another think is that when you are using redux state for your search reducer then why you are also handling react state. you can directly pass the searched params into your action. You can get that update instantly from your reducer state.
It will help your code simplicity.
This answer adds a bit onto what Aniket Nandan pointed out -- you are using setState inside of your React component instead of relying on props from above.
The main purpose of using Redux is to take your state and put it in a container alongside the application. The benefit in doing so is that your state can be shared across multiple components and you can then pass things via props into the components in your component tree.
The use of a state container (which has always reminded me a bit of using state machines) allows you to build your application without the complication of having to hand callbacks down through the tree to get changes to go back up to the top. Instead, Redux handles the changes to state and hands everything back off to React through props.
_onSearchChange(event) {
this.setState({term: event.target.value});
this.props.search(this.state.term);
}
On that note, looking at the code above, from your post, I am wondering why you setState to the term, but then always call a function you received through props? Does the search then call up to the dispatch method from the Redux store? If not, then you are not quite wired up correctly.
Further to that point, if you used connect
correctly (from react-redux
), then you should have the dispatch method from the Redux store available (this happens through context).
The way Redux is designed to work is something more like this:
_onSearchChange(event) {
this.props.dispatch({ type: 'SEARCH', term: event.target.value });
}
What would then happen is Redux should handle the state updates, and the new state will flow from Redux to React via props (this happens through all the connections under the hood in Redux, which is pretty awesome!).
const SearchReducer = (state = '', action) => {
switch (action.type) {
case SEARCH:
return action.term;
break;
default:
return state;
}
}
Based on your reducer (copied above ^^) and your use of combineReducers
, you should end up with a state that has { search: { term: 'ab' } }
if you typed 'ab', and it should be 'a' after typing only 'a', rather than lagging behind.
What was actually happening with your use of setState
and the method coming from props was the initial state (I'm assuming ''
) was being passed to this.props.search
, the first time. Meanwhile, state was updating on the component to be 'a'
after you typed 'a'. Then, the next time, when you typed 'b', the state was updating to 'ab', but this.props.search
was only receiving the term from state before the setState
finished executing -- while it was still 'a'.
This is actually one of the reasons why Redux is so awesome -- it provides a way to ensure that state is updated properly based on user actions within its own container, so that you can guarantee things are in sync across your application.
On that note, I will leave a piece of advice for working with Redux: You should rarely find yourself using setState
within a React component that is in an application using Redux. The decision of when to use setState
within a component should hinge on whether the piece of state you want to set only exists within that component and the rest of the application does not need to know about it. I cannot think of an example, and any I could offer would probably be quite contrived. To that point, the reason I can't think of one is because I think it is quite a rarity that you would have such a use case in an application.
So, you should be sticking to only using the dispatch method from the Redux store and allowing Redux to handle the state updates through its workflow and allow state to flow downward through props to the components. Adhering to this flow will prevent a lot of these weird "states getting out of sync" types of issues that can happen.
I found the solution.
React - State not updated
It appears that the setState is actually not instantly setting the state.
So I used a callback function instead.
_onSearchChange(event) {
this.setState({term: event.target.value}, function () {
console.log(this.state.term)
});
}
Now when I type 'a' it will log 'a'!