Redux store not being passed to props on component - javascript

Learning Redux and React, and I'm having an issue where I have the store created, and passed over to my <Provider> through react-redux, but I get an empty object when logging in the console.
import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import uuid from 'uuid';
var defaultState = {
tasks: [{
key: uuid.v4(),
name: 'learn Redux',
description: 'Learn how to create a completely statefully managed application.',
priority: 1,
notes: [{
key: uuid.v4(),
content: 'Creation of the store is paramount. One must import {createStore, applyMiddleware from redux package}, then define the root reducer, and create the store with applymiddleware, and then export the store.'
}],
}, ]
};
var root = (state = defaultState, action) => {
return state;
};
var store = createStore(root, applyMiddleware(thunk,logger));
export default store;
I think the issue may lie with how I'm passing it to the <Provider> component, but that also could be wrong. Just for good measure, here is my App component.
import React, { Component } from 'react';
import './App.css';
import store from './store/createStore';
import { Provider } from 'react-redux';
class App extends Component {
render() {
console.log(this.props);
// let tasks = this.props.tasks.map(x => {
// return <p>{x.name}</p>
// })
return (
<Provider store={store}>
<h1>Nothing to see here.</h1>
</Provider>
);
}
}
export default App;

<Provider> "provides" the store prop to components placed below it that use connect().
You can't place the <Provider> within a component's render function and change the props passed to it. It's already too late at that point. The props are what they are.
That will happen above this component in the tree, either another component or during your ReactDOM.render call.

The redux state does not automatically show up as props everywhere; and rightfully so. If that is the case, the performance would be devastating unless you have custom shouldComponentUpdate.
What you need to use is connect the redux state to your component. For your example, it'll be something like:
import { connect } from 'react-redux';
...
// Replace last line with this:
export default connect(
state => ({ tasks: state.tasks }),
null,
)(App);
Now, this.props.tasks will be the tasks in your redux state.

Related

How to decide whats state i want from my 2 nested provider in react-redux

hi every one i get an idea to create application with separated Redux provider for each part of my application. in real world my application is to huge to use combine reducer and i don't want to merge my all Redux to 1 store so i create a store for each part. but i need another provider to make some state global and i call this provider as parent of my application. i can see redux data from my both provider in Redux extension.
But i face with a problem that's i try use useSelector in my child component and i just can access to data of the child provider and i can't have access to my parent provider is there any solution for it or is this good way because i have hug application and hug state.
the parent provider
import { globalStore } from "#/store/globalStore";
import type { AppProps } from "next/app";
import { Provider } from "react-redux";
function MyApp({ Component, pageProps }: AppProps) {
return (
<Provider store={globalStore}>
<Component {...pageProps} />;
</Provider>
);
}
export default MyApp;
the child provider
import type { NextPage } from "next";
import { MainLayout } from "#/layouts/index";
import { Provider } from "react-redux";
import { homeStore } from "#/store/homeStore";
const Home: NextPage = () => {
return (
<>
<Provider store={homeStore}>
<MainLayout>
<h1>test</h1>
</MainLayout>
</Provider>
</>
);
};
export default Home;
and the way i create store for each provider
import { applyMiddleware, createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
import { homeReducer } from "./reducer";
export const homeStore = createStore(
homeReducer,
composeWithDevTools(applyMiddleware(thunk))
);
also i think i have problem with my dispatch action too if anyone can help with this too
In Redux, there are no concept of "parent" or "child" providers. Redux is made to have one store. Your "child provider" is just the new provider, overwriting the parent for all child components. You cannot just switch between them.
That said, there is a documented way of using 2 different contexts to use 2 stores, but generally this is not a recommended thing to do.

how to handle redux props in class methods

in my react App i'm using redux with redux-thunk.right now i'm getting props in my component but i'm unable to access latest props in my component methodsso i used componentWillReceiveProps to get latest props using nextprops then i'm saving nextprops into my states but the problem here is setState is asynchronous so when i'm fetching particular state in class methods,getting prev state value instead of nextprops value which is saved in state. but when i'm console those state in class methods using setInterval getting latest state value because setState value now saved.below is my code
Action creator
export function pickup(latlng) {
return function(dispatch) {
dispatch({ type: PICKUP_STATE,payload:latlng });
};
}
Reducer
import {
PICKUP_STATE,
PICKUP_ADD,
DROPOFF_STATE
} from '../actions/types';
export default (state={},action) => {
const INITIAL_STATE = {
pickup: '',
pickupAdd:''
};
switch(action.type) {
case PICKUP_STATE:
console.log(action.payload)
return {...state,pickup:action.payload};
case PICKUP_ADD:
return{...state,pickupAdd:action.payload};
case DROPOFF_STATE:
return {...state,dropoff:action.payload}
default:
return state;
}
//return state;
}
component
import {
connect
} from "react-redux";
import * as actions from "../actions"
class Map extends React.Component {
componentWillReceiveProps(nextprops) {
if (nextprops.pickupProps !== undefined) {
this.setState({
pick: nextprops.pickupProps
}, () => {
console.log(this.state.pick);
});
}
}
isPickEmpty(emptyPickState) {
this.props.pickup(emptyPickState);
// setTimeout(() =>{ console.log('sdkjlfjlksd',this.state.pick)
},3000);
console.log(this.state.pick);
}
}
const mapStateToProps = (state) => {
// console.log(state.BookingData.pickup);
return {
pickupProps:state.BookingData.pickup,
pickupAddProps: state.BookingData.pickupAdd
}
}
export default connect(mapStateToProps,actions)(Map);
App Root file
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import "normalize.css/normalize.css"
import "./styles/styles.scss";
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import reduxThunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import AppRouter from './routers/AppRouter';
import reducers from './reducers';
import {AUTH_USER} from "./actions/types";
const middleware = [
reduxThunk,
];
const store = createStore(reducers, composeWithDevTools(
applyMiddleware(...middleware),
// other store enhancers if any
));
const token = localStorage.getItem('token');
if(token){
store.dispatch({type:AUTH_USER});
}
ReactDOM.render(
<Provider store={store}>
<AppRouter />
</Provider>
, document.getElementById('app'));
1- how can i access latest props in my class methods
OR
2- how can i access nextprops setState value in my class methods
OR
3- any best way to solve this situation
please any one help me out from this situation, i'm stuck in from 3 days
If I understand it correctly, you still need to add a maps to dispatch to get the updated states from the store. The action creator still needs to be called and then mount it to your class method using componenetsDidMount
componentDidMount() {
this.props.fetchPickUp();
}
const mapDispatch = dispatch => {
return {
fetchPickUp: () => dispatch(pickUp()),
};

React-redux connect() fails to pass the props

I'm trying to learn react-redux architecture, and I failed on the most basic stuff.
I created class HomePage and used react-redux connect() to connect it to store's state and dispatch.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {HomeButtonClickAction} from "./HomeActionReducer";
import {connect} from "react-redux";
class HomePage extends Component {
constructor(props) {
super(props);
console.log('HomePage props');
console.log(this.props);
this.buttonClicked = this.buttonClicked.bind(this);
}
buttonClicked() {
console.log('button cliked');
this.props.buttonClick();
}
render() {
console.log('Re-rendering...');
let toggleState = this.props.toggle ? 'ON' : 'OFF';
return (
<div>
<button onClick={this.buttonClicked}>{ toggleState }</button>
</div>
)
}
}
HomePage.propTypes = {
toggle: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired
};
const mapStateToProps = (state, ownProps) => {
return {
toggle: state.toggle
}
};
const mapDispatchToProps = (dispatch, ownProps) => {
return {
buttonClick: () => {
dispatch(HomeButtonClickAction());
}
}
};
const HomeContainer = connect(
mapStateToProps,
mapDispatchToProps
)(HomePage);
export default HomePage;
But it's not working for me. HomeContainer doesn't pass props to HomePage component.
I've got these warnings in devtools.
My index.js looks like this.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import AppReducer from "./reducers/AppReducer";
import { createStore } from "redux";
import { Provider } from 'react-redux';
const store = createStore(AppReducer);
ReactDOM.render(
<Provider store={ store }>
<App/>
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
and AppReducer.js
import { combineReducers } from 'redux';
import { toggle } from '../home/HomeActionReducer';
const AppReducer = combineReducers({
toggle
});
export default AppReducer;
and HomeActionReducer.js
const HOME_BUTTON_CLICK = 'HOME_BUTTON_CLICK';
export function toggle (state = true, action) {
console.log('toggle launched');
switch (action.type) {
case HOME_BUTTON_CLICK :
return !state;
default:
console.log('Toggle reducer default action');
return state;
}
}
export function HomeButtonClickAction() {
console.log('action emitted');
return {
type: HOME_BUTTON_CLICK
};
}
Being a newbie I'll really appreciate your help :)
You are exporting HomePage, which is the presentational component. You want to export HomeContainer, which is the container that passes the props to HomePage through connect.
So replace this
export default HomePage;
with this
export default HomeContainer;
You can also directly write
export default connect(mapStateToProps, mapDispatchToProps)(HomePage);
Note that, since it's the default export, you can name the import as you want, eg.:
import HomePage from './HomePage' // even if it's HomeContainer that is exported
You have this:
const HomeContainer = connect(
mapStateToProps,
mapDispatchToProps
)(HomePage);
export default HomePage;
To create an instance of the connect component you need to do this:
export default connect()(HomePage);
Notice I did not write export default twice, bad practice, you only export default once per component so the connect() goes inside that same line of code and the invocation or second set of parentheses you wrap around the component you are working in.
This connect() function is actually a React component that you are going to pass some configuration to and the way you begin to do that is by calling mapStateToProps like so:
const mapStateToProps = () => {
};
export default connect()(HomePage);
You could also do:
function mapStateToProps() {
}
If you read it, it makes sense, this is saying that we are going to map our state object, all the data inside the redux store and run some computation that will cause that data to show up as props inside our component, so thats the meaning of mapStateToProps.
Technically, we can call it anything we want, it does not have to be mapStateToProps, but by convention we usually call it mapStateToProps and its going to be called with all the state inside of the redux store.
const mapStateToProps = (state) => {
};
export default connect()(HomePage);
The state object contains whatever data you are trying to access from the redux store. You can verify this by console logging state inside the function like so:
const mapStateToProps = (state) => {
console.log(state);
return state;
};
export default connect()(HomePage);
I am returning state just to ensure that everything is working just fine.
After defining that function, you take it and pass it as the first argument to the connect() component like so:
const mapStateToProps = (state) => {
console.log(state);
return state;
};
export default connect(mapStateToProps)(HomePage);
Thats how we configure the connect component.We configure it by passing it a function. Run that and see what happens.

state.getStore() or mapStateToProps - React.js / Redux

So I'm building my first Redux project to learn it. I just got it to output updated state in the middleware logger so that was great.
Now I am trying to have the component update based on the updated state.
Obviously I'm missing some fundamental concept here...I think I'm supposed to mapStateToProps but I just don't understand it.
Isn't there some simple global thing I can use?
How come when I console out store.getState().quote it works but doesn't update the component?
QUOTE COMPONENT
import React from 'react';
import { connect } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import promise from 'redux-promise';
import createLogger from 'redux-logger';
import quoteReducer from './reducers/quoteReducer';
const logger = createLogger();
const store = createStore(
quoteReducer,
applyMiddleware(thunk, promise, logger)
);
class quote extends React.Component {
componentWillMount() {
let req = new XMLHttpRequest();
req.responseType = "json";
req.onload = function(){
store.dispatch({type: "ADD_QUOTE", text: req.response.quote, movie: req.response.author});
}.bind(this)
req.open("GET", "http://localhost:3001/quote");
req.send();
}
render(){
return (
<div id="quote">
<h1> "{store.getState().quote}" </h1>
<h2> -{store.getState().author} </h2>
</div>
)
}
}
export default connect()(quote);
REDUCER
const quoteReducer = (state = {quote: "", author: ""}, action) => {
switch (action.type) {
case 'ADD_QUOTE':
return {
quote: action.text,
author: action.movie
}
default:
return state
}
}
export default quoteReducer;
ACTION
export const addQuote = (text, movie) => {
return {
type: "ADD_QUOTE",
text,
movie
}
}
APP.JS
import React, { Component } from 'react';
import Quote from './quote';
import AnswerInput from './answerInput';
import logo from './logo.svg';
import './App.css';
import './quote.css';
import './answerInput.css';
import './assets/styles/global.css';
class App extends Component {
render() {
return (
<div className="App">
<Quote />
<AnswerInput />
</div>
);
}
}
export default App;
INDEX.JS
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import quoteReducer from './reducers/quoteReducer.js';
import './index.css';
let store = createStore(quoteReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
The answer from #Konstantin Vitkovsky is sort of right, but not exactly.
There's a few problems with your original example:
First, your quote component should have a capitalized name, like Quote. React and JSX use capitalization to know whether you're trying to render a component or an individual tag. First letter uppercase means it's a component, first letter lowercase means it must be a tag.
Second, your component is currently accessing the store directly, and calling store.getState() in its render method. This will work the first time, but because your component has no idea when the store updates, it will not trigger a re-render. (Also, in React, ideally a render() method should only render things based on this.props and this.state.)
Third, you are calling the React-Redux connect method at the end of your file, but you're not actually using it in any way.
Finally, you definitely shouldn't refer to the store directly in the component. You should render a <Provider store={store}> component at the top of your component tree, which will make the store accessible to any connected component inside of it.
As #Konstantin Vitkovsky said, your component will not know that the store has been updated unless your component subscribes to the store. However, that is exactly what connect already does for you. Every time the store updates, the wrapper component generated by connect will re-run the mapStateToProps function you gave it, and pass the data that you return into your "real" component.
So, for your example, your code should look roughly like this:
import React, {Component} from "react";
import ReactDOM from "react-dom";
import {Provider, connect} from "react-redux";
const store = createStore(rootReducer);
// Normally the store definition and the component would be
// defined in different files
class Quote extends Component {
render() {
const {quote, author} = this.props;
return (
<div id="quote">
<h1>{quote}</h1>
<h2>{author}</h2>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
quote : state.quote,
author : state.author
};
}
const ConnectedQuote = connect(mapStateToProps)(Quote);
ReactDOM.render(
<Provider store={store}
<Quote />
</Provider>,
document.getElementById("root")
);
I'd encourage you to read through the Redux docs thoroughly - they contain a lot of good information. Also, I keep a big list of links to high-quality tutorials and articles on React, Redux, and related topics, at https://github.com/markerikson/react-redux-links . It's specifically intended to be a great starting point for anyone trying to learn the ecosystem.
I ended up getting some help from a contact and this is the code that worked!
import React from 'react';
import { connect } from 'react-redux';
import { addQuote } from './actions';
const mapStateToProps = (state) => {
const { quote } = state;
return quote; //This would be the job for selectors (from advanced redux tutorial)
}
const mapDispatchToProps = (dispatch) => ({
addQuote(req) {
const { quote, author } = req.response;
dispatch(addQuote(quote, author)); // Use the action creator
}
})
class Quote extends React.Component {
componentWillMount() {
let req = new XMLHttpRequest();
req.responseType = "json";
req.onload = function(){
this.props.addQuote(req);
}.bind(this)
req.open("GET", "http://localhost:3001/quote");
req.send();
// the whole bit above is best replaced with a redux-fetch middleware
}
render(){
const {quote, author} = this.props;
return (
<div id="quote">
<h1> "{quote}" </h1>
<h2> -{author} </h2>
</div>
)
}
}
const ConnectedQuote = connect(mapStateToProps, mapDispatchToProps)(Quote);
export default ConnectedQuote;

React + Redux: Component does not update

Trying out React + Redux, and probably am doing something obviously stupid, because a component that fires an action to fetch data over the network does not get updated (re-rendered) when the data is fetched.
Here are the relevant bits of my code:
The top-level index.js serving as an entry point for the app:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, browserHistory } from 'react-router';
import reduxPromise from 'redux-promise';
import createLogger from 'redux-logger';
const logger = createLogger();
import routes from './routes';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(reduxPromise, logger)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<Router history={browserHistory} routes={routes} />
</Provider>
, document.querySelector('.container'));
Top-level container App:
import React, {Component} from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../actions';
import Header from '../components/header';
import Showcase from '../components/showcase';
function mapStateToProps(state) {
return {
resources: state.resources
}
}
function mapDispatchToProps(dispatch) {
return {
fetchResources: () => {
dispatch(Actions.fetchResources());
}
}
}
class App extends Component {
render() {
console.log('props in App', this.props);
return (
<div>
<Header/>
<Showcase
fetchResources={this.props.fetchResources}
resources={this.props.resources}
/>
</div>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
Component that triggers an action to sends a request for data when it is about to mount and is supposed to show the fetched data:
import React, {Component} from 'react';
import {connect} from 'react-redux';
class Showcase extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchResources();
}
render() {
console.log('resources', this.props);
return (
<div>
This is showcase
</div>
);
}
}
export default connect(state => ({resources: state.resources}))(Showcase)
Action Creator:
import * as types from '../constants/ActionTypes';
import axios from 'axios';
export function fetchResources() {
return {
type: types.FETCH_FIRST,
payload: axios.get('/sampledata/1.json')
}
}
Reducer for the fetch action:
import * as types from '../constants/ActionTypes';
export default function resourcesReducer (state={}, action) {
switch (action.type) {
case types.FETCH_FIRST:
console.log('about to return', Object.assign (state, {resources: action.payload.data }))
return Object.assign (state, {resources: action.payload.data });
default:
return state
}
};
and finally the root reducer:
import { combineReducers } from 'redux';
import navigationReducer from './navigation-reducer';
import resourcesReducer from './resources-reducer';
const rootReducer = combineReducers({
navigationReducer,
resourcesReducer
});
export default rootReducer;
So, here is what I am observing. The action to request data is successfully triggered, a request is sent, the reducer receives it when the promise is resolved, and updates the state with the fetched data. At this point, I would expect the top-level App component and the Showcase component to detect that the store has updated, and to re-render, but I do not see it in the console.
Also, I am confused by redux-logger’s console output:
Specifically, I am surprized to see that the state contains reducers from the rootReducer — I don't know if it's right (an example on Redux logger Github page shows a state without reducers). It also seems surprising that the prev state as reported by redux-logger contains the same resourcesReducer object as the next state, although intuitively I would expect prev state to be more or less empty.
Could you please point out what I am doing wrong and how to get React components respond to the state changes?
==================================================
UPDATED:
1) Changed the mapStateToProps function in the App component so that it correctly maps to reducer states:
function mapStateToProps(state) {
return {
resources: state.resourcesReducer
}
}
2) Still passing the resources down to the `Showcase component:
render() {
console.log('props in App', this.props);
return (
<div>
<Header navigateActions={this.props.navigateActions}/>
React simple starter
<Showcase
fetchResources={this.props.fetchResources}
resources={this.props.resources}
/>
</div>
);
3) Trying to display resources on the screen by stringifying it to see what’s actually inside this object:
render() {
console.log('resources', this.props);
return (
<div>
This is showcase {JSON.stringify(this.props.resources)}
</div>
);
}
See this on the screen: This is showcase {}. The component does not seem to re-render.
Here’s the screenshot of the console showing that App’s props have updated with the values from the next state. Still, that did not cause the component to re-render:
UPDATED AGAIN: And my javascript-fu was poor, too. I did not quite realize that by returning Object.assign (state, {resources: action.payload.data }); I was in fact mutating the state, and that a simple inversion of arguments would let me achieve what I intended. Thanks to this discussion on SO for enlightenment.
I am surprized to see that the state contains reducers from the rootReducer
This is how it works. Take a closer look at combineReducers().
const rootReducer = combineReducers({
navigationReducer,
resourcesReducer
});
Recognise that it's not a list of parameters; it's a single object parameter. Perhaps it is clearer in verbose syntax:
var rootReducer = combineReducers({
navigationReducer: navigationReducer,
resourcesReducer: resourcesReducer
});
The resourcesReducer key points to the state returned by the resourcesReducer() function. That is, the state variable within the resourcesReducer() is just one part of the entire state.
The functions passed to connect() take the entire state as an argument. What yours should actually look like is this:
export default connect(state => ({
resources: state.resourcesReducer.resources
}))(Showcase);

Categories