How to await a useDispatch value in React? - javascript

In my React codebase I've integrated Redux usign react-redux and I'm using the useDispatch hook to dispatch actions from my views. I just have one problem I don't have access to the updated values just after updating the store. Is there a way for me to wait for the state update to complete like await or something like a callback where I can execute the other code?
This is how my code looks like:
import React from 'react';
import { useDispatch } from "react-redux";
import { setCore } from "../store/global";
// the value from the store is drilled down as props
export default function Home({ instance, core }) {
const dispatch = useDispatch();
const onConnect = async () => {
// core may have some value before but I want to clear
console.log(core); // prints the old value
await dispatch(setCore(null));
console.log(core); // Need to have null here
}
return (
<div>
<button onClick={onConnect}>Connect</button>
</div>
);
}

Dispatching by default is 100% synchronous. There's no need to wait for the dispatch itself.
However, waiting for the component's props to update is a completely different question. Given that code, core will never update, because the definition of onConnect has already captured the value of core at the time the function was defined.
You may want to move that portion of the logic into a useEffect() hook that will trigger when the value of core has changed.

Related

How to handle asynchronous fetch from Database with Gatsby or React functional component

I am building a Gatsby app, that's mainly written in React. I have a LoggedIn component where I would grab all books that the user has borrowed and display the status on the website. I use Firebase. I hope that within the LoggedIn component, I can fetch the books. But I am not sure how to wait for the fetch method is done. When I use async/await, it just broke because then my functional component would return a Promise instead of a JSX:ELEMENT type. How can I handle this problem?
import React, { useState } from 'react'
import {fetchUserBook} from "../../firebase/firebaseService"
const LoggedIn = ({user}) => { //if I put async before user,
//my LoggedIn component will return a promise, not a JSX component, which will break my code.
const[books,setBooks] = useState([])
fetchUserRestaurant(user.email).then((info) => setBooks(info))
const renderloggedIn = () =>{
return (
<>
<h1>Welcome, {user.email}.</h1> // I hope that I can pass the "books" props here so that I can render it.
// But usually the return statement is invoked before my fetchUserRestaurant method finishes.
</>
)
}
return(
renderloggedIn()
)
}
export default LoggedIn
``
You just need to put your async fetch function inside a useEffect hook because it will be triggered once the DOM tree is loaded. Just add:
useEffect(()=>{
fetchUserRestaurant(user.email).then((info) => setBooks(info))
}, [])
Adding an empty array (deps), will make it will work as a componentDidMount(), since, in a stateless component like yours, you can't use a componentDidMount() lifecycle, you have to use hooks. This will cause a blink content until your request populates your useState hook and it is displayed. You can add a loader or whatever you like if you want to bypass it anyway.
The rest of the code seems correct.

How to access component form data in a Redux `matchDispatchToProps` function?

Edited. People have suggested passing in values to my action creator but I've tried that every way I can think of and it fails to work.
I'm currently getting my first taste of Redux and trying to get my call to mapDispatchToProps to read information in a form on button click but I'm not clear as how to get it to do so. The form component is rendered by React, and so it's a question of being able to bind when it's available but Redux is a monkey wrench I don't know how to compensate for yet. Essentially I have this for my component:
import React from 'react';
import { connect } from 'react-redux';
import { action } from '../actions/actionFile';
const Add = (props) => (
<div className="add">
<input className="field-one" type="text" placeholder="One" />
<input className="field-two" type="number" placeholder="Two" />
<input className="field-three" type="number" placeholder="Three" />
<button onClick={() => props.addItem('Literally anything')}>+</button>
</div>
)
const mapDispatchToProps = (dispatch) => {
return {
action: () => dispatch(action({
// I have three fields I need to update in the store.
}))
}
}
export default connect(null, mapDispatchToProps)(Add);
And this for my actions file:
import { ADD_ITEM } from '../constants/items';
export const addItem = (val) => {
return {
type: ADD_ITEM,
val
}
}
But if I run this and set a breakpoint inside the action creator the val value is undefined. For some reason Redux isn't letting me feed dynamic data to the action creator and I don't understand why.
Obviously I can't just pull the information with querySelector because the form doesn't exist when the callback is loaded. If I fill the object passed to the action with hard-coded dummy values it works, but I'm not able to pull in data from the form fields. I'm not even clear as to where to start with this. Any direction is greatly appreciated.
You can't access any data from Redux state, or from inside the component, in mapDispatch, because it is used as part of a wrapper component that goes around your actual component (and thus doesn't have access to anything in your component's state).
Your main options are:
Pass any necessary values as arguments into the function, like props.action(a, b, c)
Switch to using the React-Redux hooks API (useSelector and useDispatch), which lets you access data from the Redux store inside of your function component. You can then capture these values while defining a click handler.
Also, as a side note: if you are going to use connect, you should use the "object shorthand" form of mapDispatch rather than defining it as a function.
You just need to add onChange event handler to your three fields and store data of each input into your component state.
Then on button click dispatch action using this.props.action with data in your state.
In this way you can get all of your data into redux.
render() {
return <button onClick={() =>this.props.toggleTodo(this.props.todoId)} />
}
const mapDispatchToProps = dispatch => {
return { toggleTodo: todoId =>dispatch(toggleTodo(todoId)) }
}
For reference -Connect: Dispatching Actions with mapDispatchToProps ยท React Redux

Can we use redux actions inside a function that is not a functional component or a class component in reactJS

Hello I would like to know if and how it is possible to use and implement redux actions that are not inside a class or functional component.
I would like to implement something like this.
import { setPerson } from ./actions.js
export const registerPerson = (data) => {
/* here we do whatever we need to set register a person */
...
.then((res) => {
setPerson(res.data). <--- Here is the instance that I want to do.
}).catch(err => console.warn(err);
}
The goal would be to import the registerPerson function into a React component where the redux actions will fire within the imported function.
Yes, you can call your actions in then methods of something request to back-end. But make sure, that you should dispatch you action.

Should I use useselector/useDispatch instead of mapStateToProps

When creating a React app, if I use the hook useSelector, I need to adhere to the hooks invoking rules (Only call it from the top level of a functional component). If I use the mapStateToProps, I get the state in the props and I can use it anywhere without any issues... Same issue for useDispatch
What are the benefits of using the hook besides saving lines of code compared to mapStateToProps?
Redux store state can be read and changed from anywhere in the component, including callbacks. Whenever the store state is changed the component rerenders. When the component rerenders, useSelector runs again, and gives you the updated data, later to be used wherever you want. Here is an example of that and a usage of useDispatch inside a callback (after an assignment in the root level):
function Modal({ children }) {
const isOpen = useSelector(state => state.isOpen);
const dispatch = useDispatch();
function handleModalToggeled() {
// using updated data from store state in a callback
if(isOpen) {
// writing to state, leading to a rerender
dispatch({type: "CLOSE_MODAL"});
return;
}
// writing to state, leading to a rerender
dispatch({type: "OPEN_MODAL"});
}
// using updated data from store state in render
return (isOpen ? (
<div>
{children}
<button onClick={handleModalToggeled}>close modal</button>
</div>
) : (
<button onClick={handleModalToggeled}>open modal</button>
);
);
}
There is nothing you can do with mapStateToProps/mapDispatchToProps that you can't do with the useSelector and useDispatch hooks as well.
With that said, there are a couple of differences between the two methods that are worth considering:
Decoupling: with mapStateToProps, container logic (the way store data is injected into the component) is separate from the view logic (component rendering).
useSelector represents a new and different way of thinking about connected components, arguing that the decoupling is more important between components and that components are self contained. Which is better? Verdict: no clear winner. source
DX (Developer experience): using the connect function usually means there should be another additional container component for each connected component, where using the useSelector and useDispatch hooks is quite straightforward. Verdict: hooks have better DX.
"Stale props" and "Zombie child": there are some weird edge cases with useSelector, if it depends on props, where useSelector can run before the newest updated props come in. These are mostly rare and avoidable edge cases, but they had been already worked out in the older connect version. verdict: connect is slightly more stable than hooks. source
Performance optimizations: both support performance optimizations in different ways: connect has some advanced techniques, using merge props and other options hidden in the connect function. useSelector accepts a second argument - an equality function to determine if the state has changed. verdict: both are great for performance in advanced situations.
Types: using typescript with connect is a nightmare. I remember myself feverishly writing three props interfaces for each connected component (OwnProps, StateProps, DispatchProps). Redux hooks support types in a rather straightforward way. verdict: types are significantly easier to work with using hooks.
The future of React: Hooks are the future of react. This may seam like an odd argument, but change to the ecosystem is right around the corner with "Concurrent mode" and "Server components". While class components will still be supported in future React versions, new features may rely solely on hooks. This change will of course also affect third party libraries in the eco system, such as React-Redux. verdict: hooks are more future proof.
TL;DR - Final verdict: each method has its merits. connect is more mature, has less potential for weird bugs and edge cases, and has better separation of concerns. Hooks are easier to read and write, as they are collocated near the place where they are used (all in one self contained component). Also, they are easier to use with TypeScript. Finally, they will easily be upgradable for future react versions.
I think you misunderstand what "top level" is. It merely means that, inside a functional component, useSelector() cannot be placed inside loops, conditions and nested functions. It doesn't have anything to do with root component or components structure
// bad
const MyComponent = () => {
if (condition) {
// can't do this
const data = useSelector(mySelector);
console.log(data);
}
return null;
}
---
// good
const MyComponent = () => {
const data = useSelector(mySelector);
if (condition) {
console.log(data); // using data in condition
}
return null;
}
If anything, mapStateToPtops is located at even higher level than a hook call
the rules of hooks make it very hard to use that specific hook. You still need to somehow access a changing value from the state inside callbacks
To be fair you almost never have to access changing value inside a callback. I can't remember last time I needed that. Usually if your callback needs the latest state, you are better off just dispatching an action and then handler for that action (redux-thunk, redux-saga, redux-observable etc) will itself access the latest state
This is just specifics of hooks in general (not just useSelector) and there are tons of ways to go around it if you really want to, for example
const MyComponent = () => {
const data = useSelector(mySelector);
const latestData = useRef()
latestData.current = data
return (
<button
onClick={() => {
setTimeout(() => {
console.log(latestData.current) // always refers to latest data
}, 5000)
}}
/>
)
}
What are the benefits of using the hook besides saving lines of code compared to mapStateToProps?
You save time by not writing connect function any time you need to access store, and removing it when you no longer need to access store. No endless wrappers in react devtools
You have clear distinction and no conflicts between props coming from connect, props coming from parent and props injected by wrappers from 3rd party libraries
Sometimes you (or fellow developers you work with) would choose unclear names for props in mapStateToProps and you will have to scroll all the way to mapStateToProps in the file to find out which selector is used for this specific prop. This is not the case with hooks where selectors and variables with data they return are coupled on the same line
By using hooks you get general advantages of hooks, the biggest of which is being able couple together and reuse related stateful logic in multiple components
With mapStateToProps you usually have to deal with mapDispatchToProps which is even more cumbersome and easier to get lost in, especially reading someone else's code (object form? function form? bindActionCreators?). Prop coming from mapDispatchToProps can have same name as it's action creator but different signature because it was overridden in mapDispatchToprops. If you use one action creator in a number of components and then rename that action creator, these components will keep using old name coming from props. Object form easily breaks if you have a dependency cycle and also you have to deal with shadowing variable names
.
import { getUsers } from 'actions/user'
class MyComponent extends Component {
render() {
// shadowed variable getUsers, now you either rename it
// or call it like this.props.getUsers
// or change import to asterisk, and neither option is good
const { getUsers } = this.props
// ...
}
}
const mapDispatchToProps = {
getUsers,
}
export default connect(null, mapDispatchToProps)(MyComponent)
See EDIT 2 at the end for the final answer
Since no one knows how to answer, it seems like the best answer is that you should NOT be using useselector when you need information in other places other than the root level of your component. Since you don't know if the component will change in the future, just don't use useselector at all.
If someone has a better answer than this, I'll change the accepted answer.
Edit: Some answers were added, but they just emphasize why you shouldn't be using useselector at all, until the day when the rules of hooks will change, and you'll be able to use it in a callback as well. That being said, if you don't want to use it in a callback, it could be a good solution for you.
EDIT 2: An answer with examples of all that I wanted was added and showed how useSelector and useDispatch are easier to use.
The redux state returned from the useSelector hook can be passed around anywhere else just like its done for mapStateToProps. Example: It can be passed to another function too. Only constraint being that the hook rules has to be followed during its declaration:
It has to be declared only within a functional component.
During declaration, it can not be inside any conditional block . Sample code below
function test(displayText) {
return (<div>{displayText}</div>);
}
export function App(props) {
const displayReady = useSelector(state => {
return state.readyFlag;
});
const displayText = useSelector(state => {
return state.displayText;
});
if(displayReady) {
return
(<div>
Outer
{test(displayText)}
</div>);
}
else {
return null;
}
}
EDIT: Since OP has asked a specific question - which is about using it within a callback, I would like to add a specific code.In summary, I do not see anything that stops us from using useSelector hook output in a callback. Please see the sample code below, its a snippet from my own code that demonstrates this particular use case.
export default function CustomPaginationActionsTable(props) {
//Read state with useSelector.
const searchCriteria = useSelector(state => {
return state && state.selectedFacets;
});
//use the read state in a callback invoked from useEffect hook.
useEffect( ()=>{
const postParams = constructParticipantListQueryParams(searchCriteria);
const options = {
headers: {
'Content-Type': 'application/json'
},
validateStatus: () => true
};
var request = axios.post(PORTAL_SEARCH_LIST_ALL_PARTICIPANTS_URI, postParams, options)
.then(function(response)
{
if(response.status === HTTP_STATUS_CODE_SUCCESS) {
console.log('Accessing useSelector hook output in axios callback. Printing it '+JSON.stringify(searchCriteria));
}
})
.catch(function(error) {
});
}, []);
}
For callback functions you can use the value returned from useSelector the same way you would use the value from useState.
const ExampleComponent = () => {
// use hook to get data from redux state.
const stateData = useSelector(state => state.data);
// use hook to get dispatch for redux store.
// this allows actions to be dispatched.
const dispatch = useDispatch();
// Create a non-memoized callback function using stateData.
// This function is recreated every rerender, a change in
// state.data in the redux store will cause a rerender.
const callbackWithoutMemo = (event) => {
// use state values.
if (stateData.condition) {
doSomething();
}
else {
doSomethingElse();
}
// dispatch some action to the store
// can pass data if needed.
dispatch(someActionCreator());
};
// Create a memoized callback function using stateData.
// This function is recreated whenever a value in the
// dependency array changes (reference comparison).
const callbackWithMemo = useCallback((event) => {
// use state values.
if (stateData.condition) {
doSomething();
}
else {
doSomethingElse();
}
// dispatch some action to the store
// can pass data if needed.
dispatch(someActionCreator());
}, [stateData, doSomething, doSomethingElse]);
// Use the callbacks.
return (
<>
<div onClick={callbackWithoutMemo}>
Click me
</div>
<div onClick={callbackWithMemo}>
Click me
</div>
</>
)
};
Rules of hooks says you must use it at the root of your component, meaning you CANT use it anywhere.
As Max stated in his answer just means that the hook statement itself must not be dynamic / conditional. This is because the order of the base hooks (react's internal hooks: useState, etc) is used by the backing framework to populate the stored data each render.
The values from hooks can be used where ever you like.
While I doubt this will be close to answering your complete question, callbacks keep coming up and no examples had been posted.
not the answer but this hook can be very helpful if you want to get decoupled nature of mapDispatchToProps while keeping simplicity and dev experience of hooks:
https://gist.github.com/ErAz7/1bffea05743440d6d7559afc9ed12ddc
the reason I don't mention one for mapStatesToProps is that useSelector itself is more store-logic-decoupling than mapStatesToProps so don't see any advantage for mapStatesToProps. Of course I dont mean using useSelector directly but instead create a wrapper on it in your store files (e.g. in reducer file) and import from there, like this:
// e.g. userReducer.js
export const useUserProfile = () => useSelector(state => state.user.profile)

How to pass an new props from a parent component with React and Redux

This is my component's class:
import React, { Component } from 'react';
import {connect} from 'react-redux';
import Button from '../UI/Button/Button';
import * as actions from '../../store/actions';
class Password extends Component {
submitPassword=(e)=>{
e.preventDefault();
this.props.submitPassword(this.state.password, this.props.levelNumber);
}
render() {
<Button clicked={this.submitPassword} >
Submit password
</Button>
}
}
const mapStateToProps = state => {
return {
};
}
const mapDispatchToProps = dispatch => {
return {
submitPassword: (password,levelNumber) => dispatch(actions.submitPassword(password,levelNumber))
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Password);
and this is my action:
export const submitPassword = () => {
// HERE ALL MY LOGIC !!!
return {
level:undefined,
type:actions.PASSWORD
}
}
The code working all correctly including params and logic.
I wanna that every time that finish to execute the function submitPassword A third component refresh/reload with the new props. Attention! My third component is parent, not child!
It's possibile to send a command from action to component? How can I do it? I have already tried with:
componentWillReceiveProps() {
console.log("new props");
}
in my component but he can not take the event.
normally a structure my redux store as follows
{
entities: {},
ui: {},
domain:{}
}
so for example when you execute submitPassword you execute all the logic and depending on the result you can dispatch another action to update the ui or the domain part so the componentes that are connected respond to those changes.
The UI holds information about UI changes, like if you are submiting info display a progress bar or if it has to display a dialog.
The domain part holds information related to the whole webapp, like loggin info and notifications.
You don't need always to pass new props for redux state to be accessed.
As redux is having immutable state, you'll always be getting new updated state no matter the previous one. So this will enforce your component to update props to get latest state from redux. This is done by <Provider> wrapper attached on root level.
So hence your props will be having new values whenever redux state gets updated.
The lifecycle you are looking is static getderivedstatefromprops(). This lifecycle gets executed whenever props are changed/updated.
I made visual implementation on fly that can aid you the picture. Here Redux State means Redux Store

Categories