why react hook init before redux props - reactjs

this is some kind of code so be gentle :D
i have some page that use react hooks and redux and the redux state work
i checked it it load data. the scenario is i have page that load input boxes from redux thunk and save the changes with hooks and redux thunk
const MyComponent = (props) => {
const { reduxProp } = props
const [t1, setT1] = useState('')
useEffect( () => {
reduxActionLoadData // t1 , t2 data
setT1(reduxProp.t1)
setT2(reduxProp.t2)
},[])
return (
<div>
<input value={t1} onChange={ (e) => { setT1(e.target.value) } />
<input value={t2} onChange={ (e) => { setT2(e.target.value) } />
</div>
)
}
const mapStateToProp = (state) => (
{
reduxProp: state.reduxProp
})
const mapDispatchToProp = (dispatch) => (
{
reduxActionLoadData = connectActionCreators(reduxActionLoadDataAction, dispatch)
})
export default const connect(mapStateToProps, mapDispatchToProps)(MyComponent)
when i check props it is loaded, redux state is loaded too
but hooks init with empty value
i try to init them with reduxProp.t1, reduxProp.t2 no luck there too
the thing is when i run app it works fine but when i refresh the page it fails
and sometimg it fails when i run app too
i try to use loading state and do stuff after loading = true no luck the either

For one, your mapDispatchToProp currently does not seem to do anything, since with the = it will be parsed as a method body, not as an object - so you are not connecting your actions at all. It should look more like this:
const mapDispatchToProp = (dispatch) => (
{
reduxActionLoadData: connectActionCreators(reduxActionLoadDataAction, dispatch)
})
Then you should instead be using the map object notation:
const mapDispatchToProp = {
reduxActionLoadData: reduxActionLoadDataAction
}
and the you also have to actually use that in your component:
useEffect( () => {
props.reduxActionLoadData()
},[])
That will still create an asynchronous effect, so reduxProp.t1 will not change immediately.
You will probably have to update those later in a separate useEffect.
useEffect( () => {
setT1(reduxProp.t1)
},[reduxProp.t1])
All that said, there is no reason to use connect at all, since you could also use the useSelector and useDispatch hooks. connect is a legacy api that you really only need to use with legacy class components.
Generally I would assume that you are right now learning a very outdated style of Redux by following an outdated tutorial. Modern Redux does not use switch..case reducers, ACTION_TYPE constants, hand-written action creators, immutable reducer logic or connect - and it is maybe 1/4 of the code of old-style Redux.
Please follow the official Redux tutorial for an understanding of mondern Redux. https://redux.js.org/tutorials/essentials/part-1-overview-concepts

Related

Using the Context API as a way of mimicking useSelector and useDispatch with redux v5

I'm working on a React project where I'm constrained to using React Redux v5, which doesn't include useDispatch and useSelector.
Nonetheless I really would like to have these hooks (or something like them) available in my app.
Therefore, I've created a wrapper component at the top level of the app which I connect using redux's connect function.
My mapStateToProps and mapDispatchToProps then just look like this:
const mapDispatchToProps = (dispatch: DispatchType) => {
return {
dispatch,
};
};
const mapStateToProps = (state: StateType) => {
return {
state,
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MainLayout);
In my wrapper component, I then pass the dispatch and the state into the value:
<DispatchContext.Provider value={{ state, dispatch }}>
{children}
</DispatchContext.Provider>
Finally, I have a hook that looks like this:
const useSelectAndDispatch = () => {
const context = useContext(DispatchContext);
if (context === null) {
throw new Error("Please use useDispatch within DispatchContext");
}
const { state, dispatch } = context;
function select(selector) {
return selector(state);
}
return { dispatch, select };
};
I then use dispatch and selector in my components via useSelectAndDispatch.
I was wondering if this is an appropriate way to go about this issue, and whether I can expect any performance problems. I am using reselect, and have a good understanding of memoization. I'm just looking for opinions, since I've heard that the redux team held off implementing useDispatch and useSelector for a long time because of performance issues.
Many thanks for any opinions!
This will cause significant peformance problems. Your mapStateToProps is using the entire state object, so every time anything changes in the state, the provider must rerender. And since the provider rerendered with a new value, so too must every component that consumes the context. In short, you will be forcing most of your app to rerender anytime anything changes.
Instead of using mapStateToProps and mapDispatchToProps, i would go back to the actual store object, and build your hooks from that. Somewhere in your app is presumably a line of code that says const store = createStore(/* some options */).
Using that store variable, you can then make some hooks. If i can assume that there's only one store in your app, then the dispatch hook is trivial:
import { store } from 'wherever the store is created'
export const useMyDispatch = () => {
return store.dispatch;
}
And the selector one would be something like this. It uses .subscribe to be notified when something in the store changes, and then it uses the selector to pluck out the part of the state that you care about. If nothing changed, then the old state and new state will pass an === check, and react skips rendering. If it has changed though, the component renders (only the component that called useMySelect plus its children, not the entire app)
export const useMySelector = (selector) => {
const [value, setValue] = useState(() => {
return selector(store.getState());
});
useEffect(() => {
const unsubscribe = store.subscribe(() => {
const newValue = selector(store.getState());
setValue(newValue);
});
return unsubscribe;
}, []);
return value;
}

React: Accessing context from within a Redux store

I am a seasoned Angular developer who has now decided to learn React. To this end, I am rewriting one of my web apps in React. I have been reading about replacing Redux with the Context API and the useReducer() hook, but in my case I don't think this would be a good idea since my state is quite large and I don't yet have the React skill to mitigate any performance problems that might arise.
So I have decided to stick with Redux.
I now have the problem that I have a context that I need to make use of in my Redux store. My SocketProvider looks like this:
export const SocketProvider = ({
children,
endpoint
}) => {
const [socket, setSocket] = useState();
const [connected, setConnected] = useState(false);
useEffect(() => {
// set socket and init
}, [isAuthenticated]);
return (
<SocketContext.Provider
value={{
socket,
connected
}}
>
{children}
</SocketContext.Provider>
)
}
I would like to use the socket value from the SocketContext within my Redux store, like in this async action:
export const fetchComments = (issue: Issue): AppThunk => async dispatch => {
try {
dispatch(getCommentsStart())
const comments = socket.emit('getComment', issue.comments_url) // how can I get this socket?
dispatch(getCommentsSuccess({ issueId: issue.number, comments }))
} catch (err) {
dispatch(getCommentsFailure(err))
}
}
const App = () => {
return (
<SocketProvider endpoint={process.env.REACT_APP_API_SOCKET_ENDPOINT}>
<Provider store={store}>
{ /* my components */}
</Provider>
</SocketProvider>
);
}
I receive errors like this when I try to grab the socket context in my store code:
Unhandled Rejection (Error): Invalid hook call. Hooks can only be called inside of the body of a function component.
I can understand why this is happening, but I don't know the best way to solve it. I could keep all the thunks local to my components, or pass the context as a parameter in the async actions, but neither of these is really satisfactory.
Suggestions?

React functional component with useSelector and useDispatch creates loop

I'm sure this is a case of my brain just not getting it... BUT...
I'm used to using class Components and not functional components in general and with React Redux I'm trying to code a component that dispatches an action. The action of course causes a reducer to update the Redux state (store) as you probably know. Trying to replace mapStateToProps and mapDispatchToProps with useSelector and useDispatch however has me creating a loop... I'm guessing that I'm using useSelector incorrectly.
import { fetchPostsByTerm } from "../../_actions/_postActions";
import { useSelector, useDispatch } from "react-redux";
const payload = { vocabulary: "tags", term: "thiphif" };
export const PostsByTerm = () => {
const dispatch = useDispatch();
dispatch(fetchPostsByTerm(payload));
const posts = useSelector((state) => state.postsByTerm);
return (
<div>
<h1 className="post_heading">Posts</h1>
{posts ? posts.map((post) => <h1>{post.entityLable}</h1>) : <h1>no posts</h1>}
</div>
);
};
maybe I am using it correctly? there are other components updating state on the same page
You must not dispatch directly in the functional component. Instead use a useEffect hook to perform a dispatch. If your objective is to only dispatch the action on initial render, pass on the dependency to useEffect as an empty array
export const PostsByTerm = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchPostsByTerm(payload));
}, [])
const posts = useSelector((state) => state.postsByTerm);
return (
<div>
<h1 className="post_heading">Posts</h1>
{posts ? posts.map((post) => <h1>{post.entityLable}</h1>) : <h1>no posts</h1>}
</div>
);
};
FYI - Commenting here because it was an "aha" moment for me to understand the purpose of the array [] in the useEffect utility. The array is used to house state variables which, when changed, will force execution of the function listed. So in my case actually since I wanted fetchPostsByTerm to execute when the selected term changed (not obvious since my example shows it hardcoded)
useEffect(() => {
dispatch(fetchPostsByTerm(payload));
}, [term])
... was what I eventually went with. And now it's working great! The articles for the selected term get fetch when a new term is selected.

What is the best way to use redux action in useEffect?

I have a React Component like shown bellow (some parts are ommited) and I'm using redux for state management. The getRecentSheets action contains an AJAX request and dispatches the response to redux which updates state.sheets.recentSheets with the response's data.
All this works as expected, but on building it throws warning about useEffect has a missing dependency: 'getRecentSheets'. But if I add getRecentSheets to useEffect's dependency array it starts to rerun indefinitely and thus freezes the app.
I've read React documentation about the useEffect hook https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies but it doesn't provide a good example for such usecase. I suppose it is something with useCallback or react-redux useDispatch, but without examples I'm not sure how to implement it.
Can someone please tell me what the most concise and idiomatic way to use redux action in useEffect would be and how to avoid warnings about missing dependencies?
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import SheetList from '../components/sheets/SheetList';
import { getRecentSheets } from '../store/actions/sheetsActions';
const mapStateToProps = (state) => {
return {
recentSheets: state.sheets.recentSheets,
}
}
const mapDispatchToProps = (dispatch) => {
return {
getRecentSheets: () => dispatch(getRecentSheets()),
}
}
const Home = (props) => {
const {recentSheets, getRecentSheets} = props;
useEffect(() => {
getRecentSheets();
}, [])
return <SheetList sheets={ recentSheets } />
};
export default connect(mapStateToProps, mapDispatchToProps) (Home);
After all, it seems that correct way will be as follows:
// ...
import { useDispatch } from 'react-redux';
import { getRecentSheets } from '../store/actions/sheetsActions';
const Home = props => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getRecentSheets());
}, [dispatch])
// ...
};
This way it doesn't complain about getRecentSheets missing in dependencies array. As I understood from reading React doc on hooks that's because it's not defined inside the component. Though I'm new to frontend and I hope I didn't mess something up here.
Passing an empty array in your hook tells React your hook function will not have any dependent values from either props or state.
useEffect(() => {
getRecentSheets();
}, [])
The infinite loop arises when you declare the dispatcher as a dependency on the hook. When the component is initialized, props.recentSheets hasn't been set, and will rerender once you make your AJAX call.
useEffect(() => {
getRecentSheets();
}, [getRecentSheets])
You could try something like this:
const Home = ({recentSheets}) => {
const getRecentSheetsCallback = useCallback(() => {
getRecentSheets();
})
useEffect(() => {
getRecentSheetsCallback();
}, [recentSheets]) // We only run this effect again if recentSheets changes
return <SheetList sheets={ recentSheets } />
};
No matter how many times Homes re-renders, you retain the memoized function to your dispatch call.
Alternatively, you may have encountered find similar patterns utilizing local state and then make your effect "depend" on sheets.
const [sheets, setSheets] = useState(recentSheets)
Hope this helps
I would add a check to see if recentSheets exists or not, using that as my dependency.
useEffect(() => {
if (!recentSheets) getRecentSheets();
}, [recentSheets])

React get state from Redux store within useEffect

What is the correct way to get state from the Redux store within the useEffect hook?
useEffect(() => {
const user = useSelector(state => state.user);
});
I am attempting to get the current state within useEffect but I cannot use the useSelector call because this results in an error stating:
Invariant Violation: Hooks can only be called inside the body of a function component.
I think I understand why as it breaks one of the primary rules of hooks.
From reviewing the example on the Redux docs they seem to use a selectors.js file to gather the current state but this reference the mapStateToProps which I understood was no longer necessary.
Do I need to create some kind of "getter" function which should be called within the useEffect hook?
Don't forget to add user as a dependency to useEffect otherwise your effect won't get updated value.
const user = useSelector(state => state.user);
useEffect(() => {
// do stuff
}, [user]);
You can place useSelector at the top of your component along with the other hooks:
const MyComponent = () => {
...
const user = useSelector(state => state.user);
...
}
Then you can access user inside your useEffects.
I found using two useEffects to works for me, and have useState to update the user (or in this case, currUser).
const user = useSelector(state=>state.user);
const [currUser, setCurrUser] = useState(user);
useEffect(()=>{
dispatch(loadUser());
}, [dispatch]);
useEffect(()=>{
setCurrUser(user);
}, [user]);
You have to use currUser to display and manipulate that object.
You have two choices.
1 - If you only need the value from store once or 'n' time your useEffect is called and don't want to listen for any changes that may occur to user state from redux then use this approach
//import the main store file from where you've used createStore()
import {store} from './store' // this will give you access to redux store
export default function MyComponent(){
useEffect(() =>{
const user = store.getState().user;
//...
},[])
}
2 - If you want to listen to the changes that may occur to user state then the recommended answer is the way to go about
const MyComponent = () => {
//...
const user = useSelector(state => state.user);
useEffect(() => {
//...
},[])
//...
}
const tournamentinfofromstore=useSelector(state=>state.tournamentinfo)
useEffect(() => {
console.log(tournamentinfofromstore)
}, [tournamentinfofromstore])
So the problem is that if you change the state inside the useEffect that causes a rerender and then again the useEffect gets called "&&" if that component is passing data to another component will result in infinite loops.and because you are also storing that data in the child component's state will result in rerendering and the result will be infinite loop.!!
Although it is not recommended, you can use store directly in your component, even in the useEffect.
First, you have to export store from where it is created.
import invoiceReducer from './slices/invoiceSlice';
import authReducer from './slices/authSlice';
export const store = configureStore({
reducer: {
invoices: invoicesReducer,
auth: authReducer,
},
});
Then you can import it to a React Component, or even to a function, and use it.
import React, { useEffect } from 'react';
import { store } from './store';
const MyComponent = () => {
useEffect(()=> {
const invoiceList = store.getState().invoices
console.log(invoiceList)
}, [])
return (
<div>
<h1>Hello World!</h1>
</div>
)
}
export default MyComponent
You can study the API for Store in here.
You can also see why this approach is not recommended in
here.
Or, if you are interested in using redux store outside a react component, take a look at this blog post.
To add on top of #Motomoto's reply. Sometimes you depend on store to be loaded before useEffect. In this case you can simply return in if the state is undefined. useEffect will rerender once the store is loaded
const user = useSelector(state => state.user);
useEffect(() => {
if(user === undefined){
return}else{
// do stuff
}}, [user]);
I'm having the same issue, The problem to the useSelector is that we cant call it into the hook, so I can't be able to update with the action properly. so I used the useSelector variable as a dependency to the useEffect and it solved my problem.
const finalImgData_to_be_assigned = useSelector((state) => state.userSelectedImg);
useEffect(()=>{
console.log('final data to be ready to assign tags : ', finalImgData_to_be_assigned.data);
}, [finalImgData_to_be_assigned ])

Resources