useReducer in customer hooks does not update the UI - reactjs

I want to use a useReducer to implement a custom hooks like useLegacyState mentioned in the official documentation to flexibly update the state in the component.
https://reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables
Only use useState or useReducer can be implemented:
https://codesandbox.io/embed/usereducer-update-any-05v82
However, I am unable to update the UI using custom hooks:
https://codesandbox.io/embed/customer-usestates-xxn01
Why?

You need to update your reducer function:
function reducer(state, action) {
return Object.assign({}, state, action);
}
The reason is
when you do Object.assign(state, action), state also gets updated with attributes from action, which causes React not being able to disguise the original state from the updated state, hence shouldComponentUpdate returns false since state and the updated state are the same.

Related

`useSelector` rerenders even when the object is the same

I am refactoring React app that was built using class syntax and connect to React hooks.
App is meant to be for debugging and one of the bugs that has to be fixed is that in the reducer file we are not returning the new state object so the component doesn't rerender.
export default function comments(state = InitialState, action) {
switch (action.type) {
case "CHANGE_SORT": {
state.sort = action.sort;
return state;
}
default:
return state;
}
}
This doesn't cause the app to rerender so the state doesn't update and that is fine. However, once I refactored the app to use React hooks and useSelector the component rerenders even with this code. Is there a way I could make it not rerender unless the new state object is returned?
This is how the store is setup:
const store = createStore(
combineReducers({
posts,
sort,
}),
{},
applyMiddleware(thunkMiddleware)
);
The first problem is that your reducer is mutating the existing state, and you can never mutate state in a Redux app.
Both useSelector and connect will cause your component to re-render if you return new references. You'd need to show your useSelector for me to give a more specific answer as to what's going on.
Also, you should be using our official Redux Toolkit package to write your Redux logic, including using the createSlice API to write your reducers. It will drastically simplify your Redux code.

Accessing redux state in ComponentDidMount

I'm facing an issue with Redux and React
I use a redux action to fetch data from an API. When the component mounts, this action is fired and populate the Redux state. I want a second action to be fired with parameters (article) from the redux state.
My issue is that when I fire the second action, the redux state is still empty, so article is null, which causes an error.
componentDidMount() {
const { targetArticle, userVisit, match, article } = this.props
targetArticle(match.params.slug);
userVisit(match.params.slug, article.title);
}
I've already checked other topics on the subject like this one, but none of them works for me. How can I achieve that?
Thanks
You'd probably have to use componentDidUpdate lifecycle method. So given that userVisit is dependent on the result of targetArticle and assuming you are looking to this.props. for the updated Redux state, something like this should get you there:
componentDidUpdate(prevProps) {
if(prevProps.article !== this.props.article) {
// Now you have access to targetArticle's result and updated Redux state
userVisit(match.params.slug, this.props.article.title)
}
}
More in the docs: https://reactjs.org/docs/react-component.html#componentdidupdate

How to use redux state with react hooks

So we recently decided to start using hooks in our current react app. We are using redux for state management and my question was how does this work with hooks?
I've read some articles where people use the context api with hooks to create a state manager but I would like to keep using redux for now.
I know the react api has a useReducer method, can this be used to dispatch redux actions? I've been looking for a tutorial/example but can't seem to find any resources online for this. I may be headed down the wrong path but would like to know if I am. Thanks.
Nothing changes with hooks when using Redux, Redux Higher Order Component has nothing to do with Hooks. useReducer isn't meant for dispatching Redux actions but for updating the component internal state the same way Redux does.
So when you use useReducer you will dispatch actions, update the state with a reducer etc. but not the Redux state! Instead, you're doing that with the component state.
A component that consumes useReducer is a standard component with an internal state (to manage input states or whatever you want) wrapped, as usual before the hooks born, in a Redux's connect HOC.
If it could be helpful you can clarify your ideas with this post
I'm not a heavy user of the Redux (I prefer MobX), so I might be overlooking certain subtle aspects. But from what I see it's pretty straightforward and React docs on hooks provide very nice example:
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter({initialState}) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</>
);
}
Instead of reducer here, you can simply use one of the related reducers from your existing state management code base. Simply import it into the component file and pass it to the useReducer as the first argument. The state that you will return from there will become a new state for the component and it will auto-rerender itself with it. Obviously you will need to disconnect such component from Redux (if it is connected). Otherwise as it was mentioned in one of the comments you will end up with redundant state management logic.
However on your place I wouldn't rewrite it for hooks without any utter necessity.

How reducers update the store

I am new to Redux and i am finding some problems with understanding concept of reducers,
I can see many examples showing it takes current state and return updated state,
My question is how it updates the Store by returning the new state ( i am finding difficult to understand the mechanism ),
can some one please explain me .
The Redux store is nothing but just an object holding all states of the application. The reducer is the only way to update the store.
The reducer is a pure function with takes an old state and returns a new state. In reducer what we need to do is that we just provide the old state which is store currently having and then the new state which we are going to change state. You can refer this for detailed explanation for reduce function.
In simple words, reducer takes existing state object updates some property passed through reducer function and returns new object state.
Following link has better explanation. This is very nice blog how to create your own redux. You will get exactly what happens in the redux store.
https://www.jamasoftware.com/blog/lets-write-redux/
this is image that i found very helpfull when i was learning the same concept.
Dispatch
When you dispatch any function it goes to all reducers and if the type of dispatch matches it will change the state of that reducer.
functionName:()=>(dispatch)({type:'some-thing-to-match',payload})
Reducers
That handle state change.
Store
Combination of all the reducers (root reducer).
const store = combineReducers({
Reducer1:r1,
Reducer2:r2,
Reducer3:r3
})
For example take the dispatch function in TodoList that matches in r1 and changes its state.Then by connect from 'react-redux' we will connect that reducers state to TodoList.
var mapStateToProps = state=>{
return:{
r1:r1
}
}
then react will react to any change in state. If state of r1 is changed then it will update that component.
Your question how it update store by returning state. Your reducer will get store(state) and function as input and change the store according to function and return the state to store.
Then we can connect our component to that store to catch any change in it.
As we can see in image. Dispatch will change the store's state.Then
you can import(connect) that reducer to see the changes in your
component.(here TodoItem is that component)
Actually this is the part that i was missing about reducers,The part i didnt catch was reducers out put has to be assigned to store property
let Action={type:'SET_VISIBILITY_FILTER',text: 'test pay load'}
//Invoking Reducer
let store=todoApp({},Action)
//Reducer
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
message: action.text
})
default:
return state
}
}

React componentDidUpdate not receiving latest props

In Redux, I'm running the following in a redux-thunk action creator:
dispatch({type: "CASCADING_PROMPT_STATUS", promptName, list: 'model', status: 'disabled'});
dispatch({type: "CASCADING_PROMPT_STATUS", promptName, list: 'model', status: 'enabled'});
This triggers the reducer twice, and I can see in the console that Redux state changes from disabled -> enabled.
In React, I have the following component which has props connected to state of which CASCADING_PROMPT_STATUS updates.
However, in my component, I'm running a check to see if state changed in componentDidUpdate(prevProps)
This doesn't trigger.
If I change the action creator to delay the second dispatch setTimeout(<dispatch...>, 1); even by one millisecond, prevProps !== this.props, which is what I expect.
My component is connected to redux like so:
const C_Component = connect(mapStateToProps, mapDispatchToProps, null, {pure: false})(Component);
Does React batch up prop changes? Why do I have to delay the second dispatch? I thought redux dispatch were synchronous.
Edit: The way I'm updating the redux state is as follows:
var newState = {};
if (!_.isUndefined(state)){
newState = _.cloneDeep(state);
}
...
case "CASCADING_PROMPT_STATUS":
newState[action.promptName][action.list].disable = action.status === 'disabled';
break;
Redux dispatches are synchronous (unless a middleware intercepts and delays the action). However, React updates are batched in most cases.
Second, your reducer is definitely mutating the state, because you're not copying every level of nesting that needs to be updated. The mutations will cause your connected component to think nothing has changed, and skip updating. See the Structuring Reducers - Immutable Update Patterns section of the docs for more details on how to properly do immutable updates.
Actually... re-reading your reducer, you are doing a deep clone, so in theory that's not mutating. However, per the Redux FAQ, deep cloning is a bad idea. Instead, you should do nested shallow updates.
Third, your shouldComponentUpdate shouldn't compare this.props vs nextProps themselves. The actual props objects themselves will definitely be different. Instead, it should compare the contents of those objects - ie, props.a !== nextProps.a && props.b !== nextProps.b, etc. This is known as a "shallow equality comparison".

Resources