got the data in reducer but can't get in component.
this is my connection code.
const mapStateToProps =state => {
return {
name:state.fbLogin.name
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(ActionCreators,dispatch);
}
export default connect(mapStateToProps,mapDispatchToProps)(Chat);
i want to access the name using this.props.name but got "undefined"
Check your store, make sure you have a reducer for fbLogin in place, the code seems to be correct, you may want to add default props so you don't get any undefined errors
restart your app ..
some time getting data from reducers give undefined after restarting the app
you can see the real data
hope this helps you
Related
I'm building a Single App that will do SSR (server side rendering) and I'm using React + Redux.
I've just started to implement Redux in this app. It was previously built app using only React's useState, useContext etc.
The fact is that sometimes I need my app code to be aware of the environment that it's running, either ON_CLIENT or ON_SERVER, to skip some window.something statement, for example.
Before Redux, I was doing the following:
index.js (this could be the index.js of my client bundle or my server bundle)
ReactDOM.render(
<App
ON_SERVER={false} // THIS IS TRUE ON SERVER CODE
ON_CLIENT={true} // THIS IS TRUE ON CLIENT CODE
... other stuff
/>
,document.getElementById('root')
);
App.js
...
const environment = {
ON_SERVER: props.ON_SERVER,
ON_CLIENT: props.ON_CLIENT
}
...
// PROVIDING IT USING REACT CONTEXT
return (
<EnvironmentContext.Provider value={environment}>
<MyComponents/>
</EnvironmentContext.Provider>
);
And then, inside some component, I can do this pattern:
SomeComponent.js
const {ON_CLIENT} = useContext(EnvironmentContext);
ON_CLIENT && window.something;
And I want to improve this pattern with Redux.
I want to keep this in the Redux store, so I can get rid of the EnvironmentContext and access it with:
const {ON_CLIENT} = useSelector((state) => state.environment);
So I've thought of doing:
index.js
const store = createStore(rootReducer, {
environment: {
ON_CLIENT: true, // THIS IS TRUE ON CLIENT CODE
ON_SERVER: false // THIS IS TRUE ON SERVER CODE
}
});
But since I don't have a corresponding reducer for this piece of state (environment), I got this error msg:
redux.js:319 Unexpected key "environment" found in preloadedState argument passed to createStore. Expected to find one of the known reducer keys instead: "auth", "appVersion", "siteData". Unexpected keys will be ignored.
NOTE: auth, appVersion and siteData are pieces of state which I have corresponding reducers for.
Here is my rootReducer:
const rootReducer = combineReducers({
auth: updateAuth,
appVersion: updateClientVersion,
siteData: updateSiteData
});
QUESTION
Can I have some piece of state that will not change, and therefore is not handled by any reducer? Or in this case I do need to set up some dummy reducer just to always return that same state? PS: It does the trick, but it feels wrong, though.
// NOTE: I will always preload this state, in the `createStore` call, so the state will never be undefined.
function returnEnvironment(state={}, action) {
return state;
}
const rootReducer = combineReducers({
auth: updateAuth,
appVersion: updateClientVersion,
siteData: updateSiteData,
environment: returnEnvironment
});
Does anybody have a better alternative to this?
I've looked at this discussion: https://github.com/reduxjs/redux/issues/1457
There are some suggestions to populate the global object, but I'd rather keep it all inside React and Redux.
PS: Sorry for the long question, but I wanted to make my use case as clear as I could, so somebody might have a better pattern.
Redux is designed for data centralisation and management such as loading some dynamic data changing it during the application lifetime and so on.Since you are not going to change that values because your application can’t run in both environments on a same time or switch between them that mean you don’t need to change them during application lifetime and if some variable should not change value it should be declared as a CONSTANT. So declare it as a constant and important whenever you need to access it.
Edit: SOLVED! Please see below.
I want my Blog component to fire the fetchBlog action creator every time the browser requests its URL, be it via a link or a refresh. I'd like to do it with the React useEffect Hook and with the React-Redux useDispatch and useSelector Hooks. However, my action only fires when following the link to the page; I do not understand why, even after reading several explanations (like the official docs).
Here is the code:
// Everything duly imported, or else VSC would yell at me
export default function Blog() {
const dispatch = useDispatch();
// slug is set here with useSelector, this always works
useEffect(() => {
dispatch(fetchBlog(slug))
}, [slug, dispatch]);
const blog = useSelector((state) => state.blogs[0]);
// return renders the blog information from the blog constant
// since the action does not fire, blog is undefined because state.blogs is an empty array
}
I know that, on refresh, fetchBlog does not fire because of Redux DevTools and also because I put a debugger there. (And the back-end logs don't show the request coming in.) The action creator itself and the reducer must be working; if they weren't, the page would not load correctly when visited through a link.
Edit: I have determined useSelector and useDispatch are not the root cause of the problem, as changing the code to use connect with mapStateToProps and mapDispatchToProps gives the same result. The issue seems to be with useEffect.
I think the problem is you are returning the call to dispatch. Functions returned from useEffect are clean up functions, so I don't think this would run on mount, or update - only before unmount. Try this:
export default function Blog() {
// ...
// Don't return from useEffect. Just call dispatch within the body.
useEffect(() => {
dispatch(fetchBlog(slug);
}, [slug, dispatch]);
// ...
}
https://reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect
I'd like to clarify what the issue was, which #Trace guided me to finding.
useEffect wasn't being called on refresh because it gets called after the component renders/returns. When refreshing, the state - including the blog data - is lost; instead of returning, a TypeError is thrown because data.title doesn't exist. So useEffect never gets the chance of being called and fetch the blog's content.
The solution to that goes like this:
export default function Blog() {
// ...
useEffect(/* ... */)
const blog = useSelector((state) => state.blogs[0]);
if (!blog) {
return <p>Loading...</p>
}
// return actual blog contents here
}
So now fetchBlog does get called, updating blog and rendering the content.
It isn't clear to me where the slug comes from.
In theory useEffect runs after every render. In case of multiple parameters it will run the callback when one of the array parameters passed in the second argument changes.
Either create a useEffect with empty array as second argument to run it 'once' (e.g. when you refresh) or check the slug value.
Edits after checking the repo:
It's not going to work because useEffect is run AFTER the render (which was included in my answer although someone downvoted it). Dispatching the call will only happen after, or not at all if the exception was thrown before (in this case a nullpointer).
You can get the slug from react-router with match, may be handy for you to know.
export default function Blog({ match }) {
const slug = match.params.slug;
etc
The git repo shows how dispatch as is added as array parameter to useEffect, which is not necessary.
The Problem
I have an application that uses this React Redux Boilerplate: https://github.com/flexdinesh/react-redux-boilerplate
I created a new page that is connected to the injected reducer + saga.
I receive following props: posts, loading, error, loadPosts and match
When I use these directly the app is working as expected. But as soon as I start to destructure the props, the app is behaving unexpectedly.
Especially with the match props.
When I do it like this:
const SubforumPage = (props) => {
useEffect(() => {
const { id: subId } = props.match.params;
console.log('props: ', subId);
}, []);
// .... other code
}
No problem everything works.
But when I do it like this:
const SubforumPage = ({match}) => {
useEffect(() => {
const { id: subId } = match.params;
console.log('props: ', subId);
}, []);
// .... other code
}
match suddenly gets undefined!
I have really no clue what so ever why this is happening. It's the first time that I see an error like this.
This specific page is set up like this in the routing file:
<Route path="/sub/:id" component={SubforumPage} />
And it's clearly working when using (props) in the function arguments but not with ({match})
Why is this? Can please someone help me out here.
What have I tried?
I continuesly started destructuring one prop after another. At first this approach works and it's still not undefined but when I get to some props, it's different which ones, it will stop working.
I think it has to do something with how I use my useEffect() hook?
I pass an empty array so it does just run when mounting. It seems like when I refresh the page, the posts are cleared out but the useEffect doesn't run anymore, so the new posts doesn't get fetched. Because hen also the console.log inside the useEffect hook is undefined doesn't even run. But for example the loading prop in console.log outside of useEffect is indeed not undefined
(But that still does not explain why it's working with (props) as argument).
Am I just using useEffect wrong?
Many thanks
Ok guys that was completely my fault. Guess I'm too tired :D. Here is what caused the problem:
I fetch my post in the useEffect hook. I also render a component where I pass in the posts. But the posts are not available because the component has to wait for the data to come in. So I completely forgot that I have to wait for the data.
Before:
return <PostsGroup posts={posts} />;
After: (correct)
return <PostsGroup posts={posts || []} />;
I had a check in place looking like this:
if (loading) return <CircularProgress />;
(before the other return). But it doesn't matter because loading is false when the component initially renders.
So I also set the initial value from loading to true (in my initialState of the reducer). So I have now two checks in place.
Sorry guys. So stupid.
I have a question that what is the difference between use getState from store directly or use mapStateToProps. Please look at me example below
import React, { Component } from 'react'
import store from '../store'
import { connect } from 'react-redux';
class Test extends Component {
constructor(props) {
super(props);
}
render() {
return (
<p>
<h1>{this.props.count}</h1>
<h2>{store.getState().reducer1.count}</h2>
</p>
)
}
}
const mapStateToProps = (state) => ({
count: state.reducer1.count
});
// export default Test;
export default connect(mapStateToProps)(Test);
Both store.getState and mapStateToProps above work normally, it still updates when state change. If we just use getState only, we don't need to use connect method.
Another point I've recognized is when use mapStateToProps with connect, in reducer we must return a new copy of object state than return that state with modification. If not, component will not update when state changed. Like this:
return Object.assign({}, state, {
count: state.count + 1,
payload: action.payload,
});
But if we use store.getState(), we can either return a new copy or the revised one. Like this:
state.count++;
state.payload = action.payload;
return state
Anyone know please explain to me, thank you.
P/S: and similar with store.dispatch vs mapDispatchToProps, those 2 will work normally, just want to know why we should use mapToProps with connect instead of call the function directly from the store.
mapStateToProps is just a helper function which is really helpful to manage the project in modular style. For example, you can even place all the logic of connect in separate files and use where you want.
Suppose if you're working on a large scale application, then guess a sorts of properties nested there. Using connect you're actually modularizing project which is very helpful for developers who watch the project.
If you don't, you're writing several lines of code in single file.
A possible problem you'll face when using getState() or dispatch() directly. See this post for a little help to make it clear.
The key benefit using connect is that you don't need to worry about when state is changed using store.subscribe(), the connect will let you know each state change whenever it gets updates.
Also, react core concept is based on props and states. Using connect allows you to get redux state as props. Using this.props :)
And ah, I remembered at what condition I accessed the store directly rather than using connect. In my project, I needed to save all the redux state in different form to somewhere and I din't need to connect it to any component. In this case, direct usage with redux store is very easy and helpful. But if we try the same with connect in this case, then we'll have a difficult time.
Thus, I would suggest you to use them in separate condition.
Use connect if you want to map with component.
Access redux store directly if you don't need to map with component.
Further, this blog will explain a bit more: react redux connect explained
Redux Flow:
Using connect with react component:
To conclude: Using connect, you use the provider and it lets the every child component to access the store by providing a provider and using store props in root app component.
Update
Thanks to #Dominic Tobias and #gabdallah for spotting my embarrassing mistake.
The correct answer is of course;
so try checking action.payload.
The other comments regarding the switch statement and the action object we're referring to errors I made in my example, which I've since corrected.
Imagine I've combined the the following two reducers;
import { combineReducers } from 'redux'
import { routerStateReducer } from 'redux-router'
import entries from './entries'
export default combineReducers({
router: routerStateReducer,
entries
})
I would like to mutate the entries state based on another part of the global state, in this case; the router state provided by redux-router in order for example to implement pagination.
How could I do something like this?
// entries.js
import { ROUTER_DID_CHANGE } from 'redux-router/lib/constants'
const initialState = {}
function entries (state = initialState, action) {
switch (action.type) {
case ROUTER_DID_CHANGE:
// How can I access `state.router` here in order to do something like this;
if (routerState.location.pathname === '/entries') {
return {
...state,
page: routerState.location.query.page || state.page,
limit: routerState.location.query.limit || state.limit
}
}
return state
}
}
Some other approaches that come to mind;
connect the router state to the Entries route component, use the componentWillMount lifecycle method to check router state and call an action creator with the page and limit values mutating the entries state in turn. This would work; however I'm using some transition middleware to call a static fetchData method on the route component prior to mounting it, so the data get's fetched, then the pagination action creator would be called afterwards; not the desired behaviour.
listen to router actions somewhere else (i.e a dedicated router redux module), call an action creator on the entries store, but I'm not sure how well this fits with redux-router or how I would get access to the router part of the global store.
don't do this at all; simply query the router state in the static fetchData method
Other useful info;
The implementation in question is Universal App heavily inspired by react-redux-universal-hot-example
Relevant deps
react 0.14.2
redux 3.0.3
react-router 1.0.0-rc3
redux-router 1.0.0-beta3
How can I achieve this or similar behaviour? Am I even thinking about this the right way?
Back in the old days before things got simpler for a developer people would listen to the popstate event on the history object ;)
It looks like the required info is on the action?
history.listen((error, nextRouterState) => {
...
store.dispatch(routerDidChange(nextRouterState));
and the action:
export function routerDidChange(state) {
return {
type: ROUTER_DID_CHANGE,
payload: state
};
}
So try checking action.payload.
However your switch statement is using action instead of action.type so there's something fishy going on there.. You shouldn't need to do action = {} either - see http://redux.js.org/docs/basics/Reducers.html