Very Strange thing in dispatch function in react-redux - javascript

my mapstatetoprop function use state "store" and the page render.
but
when I dispatch to reducer directly and override current state and return nothing "undefined" the page still rendering like i did not change the state "become undefined".
But here is the intersting thing I added console,log(state) to mapstatetoprop(state) and it reall prints undefined but after page alread mounted(1st case).
so i try to add setTimeout to dispatch function(2nd case) or attatch it to event page stop working(3rd case)
const mapStateToProps = (state)=>{
console.log("mystate : ", state);
return {searchField :state.searchField }
}
const mapDispatchToProps =(dispatch)=>{
dispatch({type : "test"});
--> " (1st case) console print state : undefined after didmount function run"
//commented setTimeout(()=>{dispatch({type : "test"});},3000);
--> "page gets error after 3 sec(2nd case)"
return {
onchange : (event)=> dispatch(setSearchField(event.target.value))
//commented , test : ()=> {dispatch({type : "test"}); console.log("test");}
-->" (3rd case) page gets error aftet trigger its event"
}
}
myreducer function
const intialState = {
searchField : ''
}
export const searchRobots = (state = intialState , action={})=>{
switch(action.type){
case "CHANGE_SEARCH_FIELD" :
return Object.assign({} , state ,{searchField : action.payload} );
case "test":
break;
default :
return state ;
}
}

you can't call dispatch inside mapDispatchToProps. You have to return only functions after calling of which you can call dispatch function or you can use bindActionCreators for avoiding unexpected behavior. It's only one correct way for predictable working of redux. Just never call dispatch inside mapDispatchToProps

Dont dispatch directly inside mapDispatchToProps, you do some thing like this :
const mapDispatchToProps = (dispatch) => ({
fetchUserDetails : () => dispatch(yourAction(value))
});
Now fetchUserDetails will be available inside props as a function, so you can destructure it from the this.props and you use it anywhere you want, like :
const { fetchUserDetails } = this.props;
fetchUserDetails();

Related

React component not re-rendering after dispatch with mapStateToProps and connect()

I have a functional React component that is wired up with Redux's connect() to listen to any changes in my redux store. If a change were to occur, I would need to re-render my page to update the data being displayed on the screen to be accurate with the data in the store.
The data in the redux store is getting updated with no problem. If I forcefully invoke a re-render by clicking on something to cause a re-render, the new data will update on the page. But you can see how this is not practical in any sense
my component that needs to be re-rendered on any store changes, looks like the following:
function Foo1(props) {
const rightTreeData = props.retrievedRData;
const leftTreeData = props.retrievedLData;
...
...
const mapStateToProps = function (state) {
console.log(state);
return {
retrievedLData: state.retrievedLData,
retrievedRData: state.retrievedRData,
loading: state.loading,
};
};
export default connect(mapStateToProps)(Foo1);
my dispatch occurs in a function, inside of another file. This is a function to build my hierarchy
const loadChildren = (
id: string,
direction: string,
retrievedLeftTree,
retrievedRightTree,
dispatch
) => {
fetch(`...`)
.then((data) => {
...
...
dispatch(UpdateRetrievedData(data, direction)); // <-------
...
...
})
.catch((err) => {
console.log(err);
});
};
Since it is a function and not a react component. I don't believe I am able to hook it up to use connect()
my store looks like the following.
ACTION
export const UpdateRetrievedData = (payload, position) => ({
type: position === "right" ? "UPDATE_RETRIEVED_DATAR" : "UPDATE_RETRIEVED_DATAL",
payload: payload,
});
REDUCER
const RetrievedLDataReducer = (state = false, { type, payload }) => {
switch (type) {
case "UPDATE_RETRIEVED_DATAL":
return payload;
default:
return state;
}
};
export default RetrievedLDataReducer;
some more information:
the dispatch is called on a button click that is nested inside of a hierarchy tree. I am unable to pull this function out and return the data like a regular function since it is baked and rendered into the HTML isolated in the function.
I have tried approaches like using my own observer-like store.subscribe(). This did work but also resulted in getting up to thousands of requests, which eventually would completely freeze the browser.

Action creator is either called on render or cannot be called at all React-Redux

I have an action creator that needs some data passed to it to update the store:
action creator:
export const addPokemonToTeam = pokemon => {
return {
type: actionTypes.ADD_POKEMON_TO_TEAM,
pokemon: pokemon
}
}
reducer snippet:
case actionTypes.ADD_POKEMON_TO_TEAM :
return {
...state,
myTeam: state.myTeam.concat([action.pokemon])
}
But when I use this code in map dispatch to props:
const mapDispatchToProps = dispatch => {
return{
onAddPokemonToTeamHandler: pokemon =>
dispatch(myTeamActions.addPokemonToTeam(pokemon))
}
}
And then pass it as an onClick function to a component:
<TeamToggle
displayStyle="add"
onClick={() => this.props.onAddPokemonToTeamHandler(this.state.pokemon)}
>
Add to my team
</TeamToggle>
Passing the prop like that, as an anonymous function, makes the code not execute at all, and I don't understand why. If I pass it without the anonymous function, it of course is executed as soon as the component is rendered. Any ideas on how to call the action creator only on click?

How to dispatch a new message in a dynamically loaded button?

I'm trying to dispatch a message whenever I change a room - which load dynamically,
I have a component that looks like that:
const Rooms = (props) => {
return(
<div className='rooms-styles'>
{rooms.length === 0 && <p>This server is missing rooms!! WERE GOING TO DIEEE!!</p>}
<div>
{rooms.length > 0 && rooms.map((roomBtn)=><button onClick={()=>{/* Need to dispatch here! */}}>{roomBtn.roomName}</button>)}
</div>
</div>
);
}
and I'm trying to change a state property (that's how it's called?) called 'room'.
What I've tried so far:
set a mapStateToProps and mapDispatchToProps like that:
selectedRoom is the selected room, being set at the button click as selectedRoom = roomBtn.roomName
const mapStateToProps = (state) => {
return {
room: selectedRoom
};
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({changeRoom}, dispatch);
};
export default connect(mapStateToProps ,mapDispatchToProps)(Rooms);
use props.dispatch(changeRoom(roomBtn.roomName)) when changeRoom is an action (didn't work because I don't have access to props inside onClick)
github page: https://github.com/Ido-Levi/Playground
You are incorrectly using the mapDispatchToProps function
https://react-redux.js.org/using-react-redux/connect-mapdispatch
You must return an object which will have the key that is the prop to the component
const mapDispatchToProps = (dispatch) => {
return {changeRoomDispatch : bindActionCreators({changeRoom}, dispatch)};
};
and then use onClick={() => props.changeRoomDispatch(roomBtn.roomName)}
Alternatively
you can use connect without the second argument for mapDispatchToProps as
export default connect(mapStateToProps)(Rooms);
and have access to the dispatch prop inside the function and then onClick={() => props.dispatch(changeRoom(roomBtn.roomName))} should work fine.
refer the link above for documentation

Redux.js - I fail to mapStateToProps with React Redux - My React Component block on the store initial state and fail to update when store.state update

I'm trying currently to pass the app.state contained to the Redux store in a React Component.
So far, this problem is still a deep mystery...
------> HERE THE GITHUB REPOSITORY OF MY CODE <------
Hope it will help to figure out what is wrong.
Abstract :
My problem is basically about mapStateToProps, is about link a Component to the state store, AFAIK the rest work very fine, but Something seems shortcut my this.props in React's Component, because either I use connect() or delete the mapStateToProps method, my Component stil display the initial state ..!
Redux resists me like an end-level's boss...
STATE OF PLAY
The provider with a store of react-redux: OK
Connect function pass to the props: OK
mapDispatchToProps works fine! So why the state fails to update the props since the connection seems well established?
I know my action is well mapped since when I delete the mapDispatch in the connect composition, the component then fails to trigger the corresponding action.
When console.log, the mapState receive effectively the store update but the Component stay blocked on initial state (tested with a "checkState" button on the component which returns the "store.getState().propertyTargeted"
HINTS :
when I delete the mapStateToProps in connect, my React.Component continue to receive the initialState,
so maybe there is an another source that overwrites my mapStateToProps, I seek for it currently
my this.props.state variable is called in the Component's constructor, maybe the constructor doesn't receive the store.updateState or something like that ? Another track to follow.
Here my combineReducer.js :
import { combineReducers } from "redux";
import {post} from "./status"
import {entry}from "./updateState";
// only one reducer active
const appReducer = combineReducers({
entry,
post
})
export default appReducer
Here my container.js :
const mapStateToProps = (state) => {
return { word: state.entry.word }
}
const mapDispatchToProps = {
postFile: postFileAction
}
const PostFileContainer = connect(mapStateToProps, mapDispatchToProps)(Component) ;
My postFile.js :
export const postFile = (word, base64Data) => dispatch => {
console.log("postFile httpRequest reached")
dispatch({
type: 'POST_WORD',
status: request
});
Axios.post("http://localhost:7500/api/files", {
"word": word,
"data": base64Data
}, {
"Content-Type": "multipart/form-data"
})
.then(res =>
dispatch({
type: 'POST_WORD',
status: success,
res
}))
.catch(err => {
dispatch({
type: 'POST_WORD',
status: error,
err
})
});
}
Here in my store.initialState :
initial state: {
"post": {},
"entry": {
"word": "initialWord"
}
}
the UPDATE_STATE_POSTWORD is provide by an other React component therefore dispatched to the store before that the bugging component trigger it own action with a updated word's entry.
Here my UPDATE_STATE_POSTWORD action snippet :
export const updateWord = word => {
return {
type: UPDATE_STATE_POSTWORD,
word
};
}
/// reducers.js part ///
postReducer.js :
export const post = (state ={}, action) => {
console.log("postStatus reached - reducer")
switch (action.status) {
case request:
console.log("Request start")
return state
case success:
switch (action.type) {
case POST_FILE:
console.log("request succeed: ", action.res)
var _id = action.res._id
// var word= action.res.word
return (Object.assign({}, state, {
_id
}))
case POST_WORD:
console.log("request succeed: ", action.res)
return (Object.assign({}, state, {
_id: ""
}))
default :
console.log(`default state on success case in
postStatusReducer`)
return state
}
case error:
console.log("request error: ", action.err)
return state
default:
return state
}
}
entryReducer.js :
const initialState = { word : "initialWord" }
export const updateStateReducer = (state= initialState, action) =>
{
switch (action.type) {
case UPDATE_STATE_POSTWORD:
var word = action.word
return (Object.assign({}, state, {
word
}))
default:
return state
}
}
Thanks
If you are using react-thunk, your action fn would receive dispatch and getState functions as arguments.
Running getState would give you actual state of the application. Recuired data would be passed to reducer and so on.
In your example RecordingAPI receives props that comes from redux only while initializing - in constructor.
You can fix your component by adding componentWillReceiveProps method
class RecordingAPI extends React.Component {
constructor(props) {
super(props);
...
this.state = {
word : this.props.word,
state: this.props
};
}
// new method that listens to props
componentWillReceiveProps (props) {
this.setState({
word: this.props.word,
state: this.props
});
}
checkState(e){
e.persist();
e.preventDefault();
e.stopPropagation();
console.dir(this.state.word)
console.dir(this.state.state)
}
render() {
...
return (
<div>
<button onClick={(e) => this.checkState(e)}> CheckState </button>
</div>
);
}
}
My current work-around is to import the store directly in my React Component then subscribe to the changes as it :
import {store} from "../App"
store.subscribe(() => {
// When state will be updated
// we will update local component state and force component to rerender
// with new data.
this.setState({
word: store.getState().entry.word // new entry.words at each update in the statge of the React.Component
});
});
ANSWER :
Assigning the store.state value to the Component's state constructor, the Component failed to update the state. So, referring to the store.state using this.props outside any assignment to the Component.state.property works like a charm*.
The trap is that storing a props in the props.constructor.state of the children works when you work only with React.js but this mechanism doesn't works for React-Redux then you have to stay the props outside any assignment in the props.constructor.state

Redux-Thunk getStore() doesn't retain state. Returning 'undefined'

This might be a question of best practices but I'd appreciate an explanation on why this doesn't work. I'm using Typescript + Redux + Thunk and trying to call actions like this:
export const requestUserDashboards = createAction<DashboardModel>(Type.REQUEST_USER_DASHBOARDS);
Dispatch in the fetch:
export const fetchDashboards = () => {
return async (dispatch: Dispatch, getState: any) => {
try {
dispatch(requestUserDashboards({
currentDashboard: getState.currentDashboard,
dashboards: getState.dashboards,
hasDashboards: false,
error: getState.error
}))
...
}
})
}
Here's the corresponding reducer:
export const dashboardReducer = handleActions<RootState.DashboardState, DashboardModel>(
{
[DashboardActions.Type.REQUEST_USER_DASHBOARDS]: (state = initialState, action): RootState.DashboardState => ({
currentDashboard: action.payload!.currentDashboard,
dashboards: action.payload!.dashboards,
hasDashboards: action.payload!.hasDashboards,
error: action.payload!.error
})
},
initialState
);
dispatch is working, however, getState doesn't correctly collect the current store state. I'm testing this by doing the following in the component receiving the updated store:
componentWillReceiveProps(nextProps: Login.Props) {
console.log(nextProps.defaultAccounts.defaultAccount);
}
Calling this in the component using:
this.props.defaultAccountActions.fetchUserDefaultAccount();
The action is working as the values from the fetch are being captured.
However, where I am using the getState.xxxx, these values are returning as undefined:
index.tsx:84 Uncaught TypeError: Cannot read property 'defaultAccount' of undefined
The initialState from my reducer is working. I can see this from doing the console.log(this.props.defaultAccounts.defaultAccount) from the componentWillMount() function.
I'm not sure what else I can provide. I think I'm actually just fundamentally misunderstanding how actions/reducers manage the store.
Questions
I am trying to get the current store values by using the getState.xxxx in the dispatch. Is this the correct way to do this?
isn't getState a function in that place? So you would need to do something
const state = getState();
and then use state inside dispatch
found in documentation, yeah it is a function at that place so you should firstly invoke a function to get state and then use it (e.g. from documentation below)
function incrementIfOdd() {
return (dispatch, getState) => {
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
dispatch(increment());
};
}
If you are using mapstatetoprops in your component you can use that to get the values from store. mapStateToProps first argument is actually the Redux state. It is practically an abstracted getState().
const mapStateToProps = function(state, ownProps) {
// state is equivalent to store.getState()
// you then get the slice of data you need from the redux store
// and pass it as props to your component
return {
someData: state.someData
}
}

Categories