My Component is not receiving the props from the redux store. I am calling the Action by an onClick event which is this. On Click the name "wrapper" is changed to "wrapper slide-menu" in the Redux Store, however, the Props are not updating when I do console.log(). , but when I check Redux dev tools, the store is shown to be updated.
<a onClick={ ()=> this.props.setName("wrapper")} ><i className="zmdi zmdi-menu ti-align-right"></i></a>
This is a part of my Component which is console logging siderbarname which is updated from the redux store.
import React, { Component } from "react";
import "../App.css";
import { Link } from "react-router-dom";
class Index extends Component {
constructor(props) {
super(props);
this.state = {
sidebarname: this.props.sidebarname
};
}
render() {
const sidebarName = this.props.sidebarname;
console.log("sidebarname is " + sidebarName);
console.log(this.props);
This is my Action.
import { connect } from "react-redux";
import * as Actions from "./indexActionTypes";
import App from "../../_layouts";
const mapStateToProps = state => ({
sidebarname: state.sidebarReducer.sidebarname
});
const mapDispatchToProps = dispatch => ({
setName: sidebarname => {
//console.log('setName is called');
//console.log('togglemargin is'.togglemargin);
if (sidebarname !== "wrapper slide-menu") {
dispatch({
type: Actions.TOGGLE_SIDEBAR,
sidebarname: sidebarname
});
}
if (sidebarname === "wrapper") {
console.log("this is called if2");
dispatch({
type: Actions.TOGGLE_SIDEBAR,
sidebarname: "wrapper slide-menu"
});
}
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
And this is my Reducer.
import * as Actions from "../../actions/indexToggle/indexActionTypes";
//const sidebarname = "wrapper slide-menu";
let initialState = { sidebarname: "wrapper" };
const sidebarReducer = (state = initialState, action) => {
switch (action.type) {
case Actions.TOGGLE_SIDEBAR:
console.log("reducer called");
console.log(state);
//console.log('action',action);
return Object.assign({}, state, {
sidebarname: action.sidebarname
});
default:
return state;
}
};
export default sidebarReducer;
This is my Store.
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "../reducers/rootreducer";
//const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
This is my App.js File.
import React, { Component } from 'react';
import './App.css';
import { Provider } from 'react-redux';
import store from './store/store';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import App from './actions/indexToggle/indexActions';
import FirstDashboard from './_layouts/views/firstDashboard';
import SecondDashboard from './_layouts/views/secondDashboard';
import ThirdDashboard from './actions/dashboardToggle/thirdDashboardToggleAction';
import FourthDashboard from './_layouts/views/fourthDashboard';
class Main extends Component {
render() {
return (
<Provider store={store}>
<Router>
<div>
<App />
<Route path='/overview1' exact strict component={FirstDashboard} />
<Route path='/overview2' exact strict component={SecondDashboard} />
<Route path='/overview3' exact strict component={ThirdDashboard} />
<Route path='/overview4' exact strict component={FourthDashboard} />
</div>
</Router>
</Provider>
);
}
}
export default Main;
I am really getting confused why it is not rendering inside the component.
However, it is getting updated in the store.
#codemt Please add below method in your code it will receive the updated props
componentWillReceiveProps(nextProps) {
const {sidebarname} = nextProps;
this.setState({sidebarname});
}
Please put inside your relative component in your case is Index
Related
My component is not rerendering after the store is changing.
I make sure that the store is actually changing by dropping him to the console with
store.subscribe() and console.log(store.getState()) but still the component is not rerendering again.
I will appreciate your help.
configureStore.js
import { createStore, combineReducers } from 'redux';
import home from '../reducers/home';
import favorites from '../reducers/favorites';
export default () => {
const store = createStore(combineReducers({
home,
favorites
}))
return store;
}
App.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from './redux/store/configureStore';
import AppRouter from './router/AppRouter';
const store = configureStore();
const jsx = (
<Provider store={store}>
<AppRouter />
</Provider>
);
ReactDOM.render(jsx, document.querySelector('#root'));
home.js (reducer)
const homeDefaultState = {
name: 'someName'
}
export default (state = homeDefaultState, action) => {
switch (action.type) {
case 'CHANGE_NAME':
return {
...state,
name: 'otherName'
}
default:
return state;
}
}
home.js (action)
export const changeName = () => ({
type: 'CHANGE_NAME'
})
Home.js (component)
import React from 'react';
import configureStore from '../../redux/store/configureStore';
import { changeName } from '../../redux/actions/home';
import { connect } from 'react-redux';
const store = configureStore();
const handleName = () => {
store.dispatch(changeName())
}
const Home = (props) => (
<div className="home">
<button onClick={handleName}>
change name
</button>
{props.home.name}
</div>
);
const mapStateToProps = (state) => ({
home: state.home
});
export default connect(mapStateToProps)(Home);
In your Home component you initialize store for second time. And bound action to this second store
const store = configureStore();
const handleName = () => {
store.dispatch(changeName())
}
At the same time with connect() you access store declared in App.jsx
Read from first but update second. Just remove second store and use mapDispatchToProps(second parameter passed to connect()) instead:
const mapStateToProps = (state) => ({
home: state.home
});
export default connect(mapStateToProps, { handleName: changeName })(Home);
I'm trying to dispatch an action, but it returns "type" of undefined. I suspect Redux Thunk is not working properly.
Before I was dispatching the same action from the parent component and it was working.
Entry point
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import configureStore from '../ConfigureStore'
import '../App.css';
import App from './theapp/theAppContainer';
const store = configureStore()
class Root extends Component {
render() {
return (
<Provider store={store}>
<App />
</Provider>
)
}
}
export default Root;
Store
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
import allReducers from './reducers/index'
const loggerMiddleware = createLogger()
export default function configureStore() {
return createStore(
allReducers,
applyMiddleware(thunkMiddleware, loggerMiddleware)
)
}
The app - routing. Before I was dispatching the action at this level and it was working.
import React, { Component } from 'react'
import Cards from '../templates/cards/CardsContainer'
import EditApp from '../pages/editApp/EditApp'
import NewApp from '../pages/NewApp'
import AppReport from '../pages/AppReport'
import { Route, Switch, HashRouter } from 'react-router-dom'
export default class TheApp extends Component {
constructor(props) {
super(props)
}
render() {
const appId = window.location.href.split('id=')[1];
return (
<HashRouter>
<Switch>
<Route exact path="/" component={Cards} />
<Route path="/app" component={EditApp} />
<Route exact path="/new" component={NewApp} />
<Route path="/report" component={AppReport} />
</Switch>
</HashRouter>
)
}
}
The container where I dispatch the action
import { connect } from 'react-redux'
import Cards from './Cards'
import {
fetchAppsData
} from '../../../actions'
function mapStateToProps(state){
return {
apps: state.apps
}
}
function matchDispatchToProps(dispatch){
return dispatch(fetchAppsData)
}
export default connect(mapStateToProps, matchDispatchToProps)(Cards)
Action
import fetch from 'cross-fetch'
import * as helpers from '../Helpers';
export const REQUEST_ITEMS = 'REQUEST_ITEMS'
export const RECEIVE_ITEMS = 'RECEIVE_ITEMS'
export function fetchAppsData() {
return (dispatch) => {
return dispatch(fetchItems())
}
}
function fetchItems() {
return dispatch => {
dispatch(requestItems())
return fetch(helpers.appData)
.then(response => response.json())
.then(json => dispatch(receiveItems(json)))
}
}
function requestItems() {
return {
type: REQUEST_ITEMS
}
}
function receiveItems(json) {
return {
type: RECEIVE_ITEMS,
items: json,
receivedAt: Date.now()
}
}
The reducer
import {
REQUEST_ITEMS,
RECEIVE_ITEMS
} from '../actions/apps-actions'
export default function apps(
state = {
isFetching: false,
items: []
},
action
) {
switch (action.type) {
case REQUEST_ITEMS:
return Object.assign({}, state, {
isFetching: true
})
case RECEIVE_ITEMS:
return Object.assign({}, state, {
isFetching: false,
items: action.items
})
default:
return state
}
}
Try changing
function matchDispatchToProps(dispatch){
return dispatch(fetchAppsData)
}
Into this:
function matchDispatchToProps(dispatch){
return {
fetchAppsData: () => dispatch(fetchAppsData())
}
}
Also the function should be called “mapDispatchToProps” but that is not important for your problem.
I believe calling
dispatch(fetchAppsData)
isn't correct, fetchAppsData is a thunk creator, not a thunk directly. Instead you would want to do
dispatch(fetchAppsData())
The translation only work on refresh. It seems because i have used a wrapper around the App.js that why its not working.
Also i tried to add a key to intlprovider the translation worked but now all my inner components get refresh.
Could there be a way to used reactintl when using an app wrapper without refreshing all inner components??
Below you can find the app.js, index.js and the app wrapper:
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './styles/global.css';
import registerServiceWorker from './registerServiceWorker';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import rootReducer from './redux/rootReducers';
import AppWrapperContainer from './containers/appWrapperContainer/appWrapperContainer';
import {localeSet} from './redux/actions/localeActions';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer, /* preloadedState, */ composeEnhancers(
applyMiddleware(thunk)
));
if(localStorage.H24Lang)
{
store.dispatch(localeSet(localStorage.H24Lang));
}
ReactDOM.render((
<Provider store={store}>
<AppWrapperContainer/>
</Provider>
),
document.getElementById('root'));
registerServiceWorker();
AppWrapperContainer.js
import React, { Component } from 'react';
import { IntlProvider, addLocaleData} from "react-intl";
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import messages from '../../messages';
import en from "react-intl/locale-data/en";
import fr from "react-intl/locale-data/fr";
import App from "../../App";
addLocaleData(en);
addLocaleData(fr);
class AppWrapperContainer extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
const {lang} = this.props
let locale =
(navigator.languages && navigator.languages[0])
|| navigator.language
|| navigator.userLanguage
|| lang
return (
// <IntlProvider locale={lang} messages={messages[lang]} key={lang}></IntlProvider>
<IntlProvider locale={lang} messages={messages[lang]} >
<App/>
</IntlProvider>
);
}
}
AppWrapperContainer.propTypes = {
lang: PropTypes.string.isRequired
}
//what reducer you need
function mapStateToProps(state) {
console.log("State is", state);
return {
lang: state.locale.lang
};
}
export default connect(mapStateToProps,null)(AppWrapperContainer);
App.js
import React, { Component } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import Home from './screens/home/home';
import { connect } from 'react-redux';
import { BrowserRouter as Router, Route,Redirect, Switch } from 'react-router-dom';
class App extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentWillMount() {
}
componentDidMount() {
}
render() {
return (
<div className="App">
<Router>
<div className = "app-main-content">
<Route exact path='/' component={Home} />
</Router>
</div>
);
}
}
//what reducer you need
function mapStateToProps(state) {
return {
};
}
function mapDispatchToProps(dispatch) {
return {
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
The key prop in IntlProvider forces React to remount the component (and all its children), but you just want them to be re-rendered (not remounted).
First confirm that your stored state is changing its locale/lang value as expected and that this change goes thougth mapStateToProps to your AppWrapperContainer component.
Then, make sure it is received in componentWillReceiveProps method and that a re-render is fired when its value changes. Then, all children will be re-rendered (if not blocked by shouldComponentUpdate method).
By the way, what is locale variable in AppWrapperContainer for?
I have a problem in my redux reducer, it does not return expected state after dispatching FETCH_BOOKS action, it returns an empty object instead of an object of state which is books that is fetched by AJAX request,
the reducer returns correct data when storing my state in array instead of object, this is so confusing, why does this happen??
These are my components
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import * as BooksAPI from './BooksAPI';
import registerServiceWorker from './registerServiceWorker';
import { createStore, applyMiddleware } from 'redux';
import { bookReducer } from './reducers/BookReducer';
import thunk from 'redux-thunk';
import {BrowserRouter as Router} from 'react-router-dom';
const middleware = [thunk];
const initialState = {};
const store = createStore(bookReducer, initialState, applyMiddleware(...middleware));
ReactDOM.render(
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
App.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import BookShelf from './components/BookShelf'
import AllShelves from './components/AllShelves'
import Header from './components/Header';
import SearchPage from './components/SearchPage';
import * as BooksAPI from './BooksAPI';
import { Route, withRouter } from 'react-router-dom';
import './App.css';
class App extends Component {
componentWillMount() {
this.props.fetchBooks();
}
render() {
console.log(this.props.books)
return (
<div className="App">
<Header />
<Route exact path="/" component={AllShelves} />
<Route path="/search" component={SearchPage} />
</div>
);
}
}
const mapStateToProps = (state) => {
return {
books: state.books
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchBooks: () => {
BooksAPI.getAll().then(books => dispatch({
type: 'FETCH_BOOKS',
books
}))
},
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App))
reducer that doesn't works
import { FETCH_BOOKS } from '../actions/Types.js';
import * as BooksAPI from '../BooksAPI'
const initialState = {
books: [],
query: ''
}
export const bookReducer = (state = initialState, action) => {
switch(action.type) {
case 'FETCH_BOOKS':
return {
...state,
books: action.books,
}
default:
return state;
}
}
The reducer that work
export const bookReducer = (state = [], action) => {
switch(action.type) {
case 'FETCH_BOOKS':
return action.books
default:
return state;
}
}
So why storing state in object doen't work and it works perfectly with array, i don't want to store my state in array, as books is not the only data i need to manage in my state!!!
I've checked all your codes and I think the problem possibly come from the redux store setup:
const initialState = {};
const store = createStore(bookReducer, initialState, applyMiddleware(...middleware));
I suggest removing the initialState:
const initialState = {}; // remove this line cuz we don't need it
const store = createStore(bookReducer, applyMiddleware(...middleware)); //fixed like this
In addition, I think you should fetch your books in the componentDidMount() lifecycle hook instead of componentWillMount(), like this:
componentDidMount() {
this.props.fetchBooks();
}
In the second example, you are fetching the value in the reducer as action.books, instead it should be action.payload because that's the key dispatched in action.
After any Action is dispatched you have to return a new state to the store so you have to return new state object for that you have to get the state now and change the books and return the new state so following code doing that
export const bookReducer = (state = initialState, action) => {
switch(action.type) {
case 'FETCH_BOOKS':
return {
...state,
books: action.books,
}
default:
return state;
}
}
but from the other reducer you return only the books array that coming from the action that is wrong way to do that
I have reactjs setup with routes but my routing is not working. When I load the page it works but when I click on the links the URL changes but the component does not render. I tried to put as much as I can in the sandbox. load with URL/admin and click on logout etc.
https://codesandbox.io/s/o5430k7p4z
index
import React, { Component } from 'react'
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { BrowserRouter, Route, browserHistory } from 'react-router-dom';
import promise from 'redux-promise';
import { createLogger } from 'redux-logger';
import App from './App'
import reducers from './reducers';
require("babel-core/register");
require("babel-polyfill");
import 'react-quill/dist/quill.snow.css'; // ES6
const logger = createLogger();
const initialState = {};
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<App/>
</BrowserRouter>
</Provider>
, document.getElementById('root'));
App
import React, { Component } from 'react'
import { Switch, Route } from 'react-router-dom';
import ReactGA from 'react-ga';
ReactGA.initialize('UA-101927425-1');
import { connect } from 'react-redux';
import { fetchActiveUser } from './actions/index';
import { bindActionCreators } from 'redux';
import {getHttpRequestJSON} from './components/HTTP.js'
import Header from './components/header';
import Logout from './components/logout';
import SideBar from './components/sidebar';
import HomeContent from './containers/home';
import Ldapuser from './components/ldapuser';
import Admin from './components/admin/admin';
function fireTracking() {
ReactGA.pageview(window.location.pathname + window.location.search);
}
class App extends Component {
constructor(props){
super(props);
this.state = {
isGuest : false,
isSupp : false,
loading: true,
version: '',
};
}
initData = () => {
let self = this;
getHttpRequestJSON('/api/user/get/user/method/is/guest/format/json?quiet=1')
.then((response) => {
let isGuest = response.body.recordset.record.isGuest;
if(isGuest){
/*$(".logo").trigger('click');
//$("#overlay").show();
$('#modalIntro').modal('toggle');
$("#modalIntro").on("hidden.bs.modal", function () {
$(".logo").trigger('click');
});*/
}
self.props.isGuest = isGuest;
self.props.loading = false;
//self.props.version = response.header.version;
self.setState({
loading : false,
version : response.header.version,
isGuest : isGuest
});
})
.catch(error => {
console.log("Failed!", error);
//$('#myModalError .modal-body').html(error);
//$('#myModalError').modal('show');
});
getHttpRequestJSON('/api/user/get/user/method/is/supp/format/json?quiet=1')
.then((response) => {
self.setState({
isSupp : response.body.recordset.record.isSupp
});
})
.catch(error => {
console.log("Failed!", error);
//$('#myModalError .modal-body').html(error);
//$('#myModalError').modal('show');
});
}
componentDidMount() {
this.props.fetchActiveUser();
this.initData();
}
render() {
return (
<div>
<Header activeUser={this.props.activeUser} loading={this.state.loading} version={this.state.version} title={`Home`} />
<SideBar />
<main>
<Switch>
<Route path='/index.html' render={()=><HomeContent activeUser={this.props.activeUser} isGuest={this.state.isGuest} isSupp={this.state.isSupp} />} />
<Route path='/home' render={()=><HomeContent activeUser={this.props.activeUser} isGuest={this.state.isGuest} isSupp={this.state.isSupp} />} />
<Route path='/logout' component={Logout}/>
<Route path='/ldapuser' component={Ldapuser}/>
<Route path='/admin' render={()=><Admin isGuest={this.state.isGuest} isSupp={this.state.isSupp}/>} />
</Switch>
</main>
</div>
);
}
}
//export default App;
function mapStateToProps(state) {
if(state.activeUser.id > 0){
ReactGA.set({ userId: state.activeUser.id });
}
// Whatever is returned will show up as props
// inside of the component
return {
activeUser: state.activeUser
};
}
// Anything returned from this function will end up as props
// on this container
function mapDispatchToProps(dispatch){
// Whenever getUser is called, the result should be passed
// to all our reducers
return bindActionCreators({ fetchActiveUser }, dispatch);
}
//Promote component to a container - it needs to know
//about this new dispatch method, fetchActiveUser. Make it available
//as a prop
export default connect(mapStateToProps, mapDispatchToProps)(App);
The codesandbox is not working, but I think what is happening to you is a very common problem when using react-redux and react-router. The connect HOC of react-redux has a builtin SCU (shouldComponentUpdate), so for it to know to rerender is requires to receive new props. This can be done using the withRouter hoc of react-router. Simply wrap connect(..)(MyComponent) with withRouter(connect(..)(MyComponent)) or do it clean and use compose (from recomponse for example);
const enhance = compose(
withRouter,
connect(mapStateToProps)
)
export default enhance(MyComponent)
Make sure not to do it the other way around, because that does not work.