React component not rerendring when state is changed with Redux - reactjs

My state is changing as I can see in console but my Book component is not refreshed on state change. I have two containers/components in my app. BookList that renders the list of all available books. Book component just gets the activeBook and display its details. I am facing the rerender issue on Book Component.
import React, {Component} from 'react';
import {connect} from 'react-redux';
class Book extends Component{
render(){
return(
<div className="col-md-9 details">
<h3>Title: {this.props.activeBook.name}</h3>
<p><b>Author:</b> {this.props.activeBook.author}</p>
<p><b>Pages:</b>{this.props.activeBook.pages}</p>
<p><b>Available:</b>{this.props.activeBook.aval}</p>
{console.log(this.props)}
</div>
);
}
}
const mapStateToProps = (state) =>{
return {
activeBook: state.active
}
}
export default connect(mapStateToProps)(Book);
My BookList Component is as following
import React, {Component} from 'react';
import {connect} from 'react-redux';
class BookList extends Component{
renderList(){
return this.props.books.map((book)=>{
return(
<li
key={book.id}
onClick={() => this.props.dispatch({type:'CHANGED',activeBook:book})}
className="list-group-item">
{book.name}
</li>
);
});
}
render(){
return(
<div className="col-md-3">
<ul className='list-group'>
{this.renderList()}
</ul>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
books:state.books
}
}
export default connect(mapStateToProps)(BookList);
My App.js is as follows:-
import React, { Component } from 'react';
//Import All Components Here
import BookList from './containers/BookList';
import Book from './components/Book';
class App extends Component {
render() {
return (
<div className="container">
<div className="App">
<div className="row">
<BookList/>
<Book/>
</div>
</div>
</div>
);
}
}
export default App;
and lastly my bookReducer is as follows:-
const bookReducer = (state = [], action) => {
switch(action.type){
case 'CHANGED':
state.active=action.activeBook;
return state;
default:
return state;
}
}
export default bookReducer;
I can exactly see in my console that State is being changed, but problem is Book Container does not respond to that change. In index.js this is my code
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
import {Provider} from 'react-redux';
import 'bootstrap/dist/css/bootstrap.min.css';
import './index.css';
import App from './App';
import bookReducer from './reducers/bookReducer';
const initialState={
books:[
{id:1,name:'Learn Java', author:'Shakeel', pages:50,aval:'Yes'},
{id:2,name:'React Native', author:'Asim', pages:212,aval:'No'},
{id:3,name:'Angular JS', author:'Tahir', pages:150,aval:'Yes'},
{id:4,name:'NodeJS', author:'Saleem', pages:120,aval:'Yes'},
{id:5,name:'C++ for Games', author:'Shakeel', pages:140,aval:'Yes'}
],
active:{id:5,name:'C++ for Games', author:'Shakeel', pages:140,aval:'Yes'}
};
const store = createStore(bookReducer,initialState);
ReactDOM.render(<Provider store={store}>
<App /></Provider>,
document.getElementById('root'));

The way you are setting the state in reducer needs to change, try this change.
const bookReducer = (state = [], action) => {
switch(action.type){
case 'CHANGED':
return {
...state,
active : action.activeBook
}
default:
return state;
}
}

Related

What's wrong in the below react redux code snippets?

I have created react app and try to run after adding simple redux stuffs as below.
Initial state value and updated state value(after button click) not appearing in the screen.
No error thrown in the console.
Expected result should be display the initial Message from store as "Please subscribe".
After clicking subscribe button, the text should be change into "Thanks for Subscribing!".
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { createStore } from 'redux';
import reducer from './Reducer';
import { Provider } from 'react-redux';
const store = createStore(reducer);
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();
Reducer.js
const initialState = {
message: "Please subscribe!"
};
const reducer = (state = initialState, action) => {
console.log(action);
const newState = { ...state };
if (action.type === "PRINT_ITEM") {
newState.message = "Thanks for subscribing!"
};
return newState;
}
export default reducer;
App.js
import { Component } from 'react';
import './App.css';
import { NewComp } from './NewComp';
class App extends Component {
render() {
return (
<div className="App">
<h1>Welcome</h1>
<NewComp />
</div>
);
}
}
export default App;
NewComp.js
import React, { Component } from 'react'
import { connect } from 'react-redux';
export class NewComp extends Component {
styles = {
fontStyle: 'italic',
color: 'purple'
}
render() {
return (
<div className="App">
<h3 style={this.styles}>{this.props.msg}</h3>
<button onClick={this.props.ButtonChange}>Subscribe</button>
</div>
);
}
}
const mapStateToProps = (state) => {
console.log(state); //Not get fired
return {
msg: state.message
};
};
const mapDispatchToProps = (dispatch) => {
console.log(dispatch); //Not get fired
return {
ButtonChange: () => dispatch({ type: "PRINT_ITEM" })
};
};
export default connect(mapStateToProps, mapDispatchToProps)(NewComp);
In App.js, you're importing the named export from NewComp, which is not the Redux-connected version. Instead, import the default export, which is Redux-connected:
import NewComp from './NewComp';

How to solve "Invalid Hook Call"?

I am learning react-redux, I couldn't understand what problem occuring when I wrap App component with Provider.
Error: Invalid Hook Call
https://i.stack.imgur.com/yFSnF.png
But If I don't wrap it's OK.
Now, My question is what's the mistake in my code?
Root Component:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import App from './components/App';
import reducers from './reducers';
const store = createStore(reducers);
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>
,
document.getElementById('root')
);
App Component:
import React from 'react';
import SongList from './songList';
const App = ()=>{
return (
<div>
<SongList />
</div>
);
};
export default App;
SongList Component:
import React from 'react';
class SongList extends React.Component{
render(){
return(
<div className='ui divided list'>
<div className='ui header'>
<div className='title'>
Song List
</div>
</div>
<div className='ui button primary'>
Select
</div>
</div>
);
}
};
export default SongList;
Reducers:
import {combineReducers} from 'redux';
const songReducer = ()=>{
return [
{title:'Zikrullah', duration: 4.05},
{title: 'Madina', duration: 5.50},
{title: 'Sunnat', duration: 3.96},
{title: 'Mandatory', duration:5.16}
]
};
const selectedSongReducer = (selectedSong = null, action) => {
if(action.type === 'SONG_SELECTED'){
return action.payload;
}
return selectedSong;
};
export default combineReducers({songs: songReducer,
selectedSong: selectedSongReducer});
Action:
export const selectSong = (song)=> {
return {
type: 'SONG_SELECTED',
payload:song
};
};
I am trying to identify the wrong part of code for two days. I couldn't identify. Please, help me to solve the issue.

React Redux cannot access props from method in onClick

I have the following for my index.js. I have set up React with create-react-app. I then installed redux and react redux.
import React from 'react';
import ReactDOM from 'react-dom';
import AppContainer from './AppContainer';
import {createStore} from 'redux'
import {Provider} from 'react-redux'
const defaultState = {
activeTab: 'firstTab'
};
const reducer = function(state=defaultState, action){
switch (action.type) {
case 'TAB_CHANGED':
return state.merge({
activeTab: state.activeTab
})
default: return state;
}
};
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.getElementById('root')
);
and the following for my AppContainer.js
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux'
class AppContainer extends React.Component {
static propTypes = {
activeTab: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired,
}
doSomething = function(){
this.props.dispatch('TAB_CHANGED', {activeTab: Math.random().toString(36).substring(7)})
}
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">{ this.props.activeTab }</h1>
</header>
<p className="App-intro">
<button onClick={this.doSomething}>Do Something</button>
</p>
</div>
);
}
}
function mapStateToProps(state){
return state;
}
export default connect(mapStateToProps)(AppContainer);
The page loads fine when first rendered. The react dom can access the this.props.activeTab. however when I click on the Do Something button I get the following error: TypeError: Cannot read property 'props' of undefined
You have to bind the doSomething function to the context of the component else it this will refer to the context of the render. So add the snippet to the constructor
constructor(){
super()
this.doSomething = this.doSomething.bind(this);
}
or ES6 - declare doSomething as an arrow function and there will be no need for bind in the constructor
constructor(){}
doSomething = () => {
....
}

passing data from actions to reducer fail in react - redux

I am new in React. As I read many documents, I realized that the state of the application should managed outside each components. And I choose Redux for my project.And I tried to pass username and password from my SigIn component. But when I click on the login button , the default statement inside the switch is always executed.The code are given below.
SignIn.jsis as below
import React from 'react';
import Header from './Head.js';
import { logIn } from '../actions/index.js';
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux';
class SignIn extends React.Component{
constructor(){
super();
this.logInClick = this.logInClick.bind(this);
}
logInClick() {
let { dispatch } = this.props;
const data = {username:'sojimon#gmail.com', password:'12345'}
// this.props.logIn(data);
this.props.dispatch(logIn(data));
}
render(){
return(
<div>
<Header/>
<br/>
<div className="col-md-4 col-md-offset-4">
<div className="well">
<h4 className="signin_header">Sign In</h4>
<div>
<div>
<label>Email:</label>
<input type="text" className="form-control" />
</div>
<div>
<label>Password:</label>
<input type="text" className="form-control"/>
</div>
<br/>
<button className="btn btn-primary" onClick={ this.logInClick }>Login</button>
</div>
</div>
</div>
</div>
)
}
}
const matchDispatchToProps = (dispatch) => ({
// logIn: (data) => dispatch(logIn(data)),
})
SignIn.propTypes = {
logIn: React.PropTypes.func
}
export default connect (matchDispatchToProps)(SignIn);
And my action/index.js as follows,
import * as types from './types.js';
export const logIn = (state, data) => {
return {
type: types.LOG_IN,
state
}
}
And reducers/logIn.js is,
import React from 'react';
import { LOG_IN } from '../actions/types.js'
const logIn = (state = [], action) => {
switch (action.type) {
case 'LOG_IN':
console.log('switch Case ==> LOG_IN');
return [
...state,
{
username: 'asdf',
password: '123',
}
]
// return action.logIn
default:
console.log('switch Case ==> Default');
return state
}
}
export default logIn
And created store in index.js file as,
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app/App';
import './app/index.css';
import Routes from './app/route';
import { createStore } from 'redux' // import store
import { Provider } from 'react-redux' // import provider
import myApp from './app/reducers/index.js'
let store = createStore(myApp);
ReactDOM.render(
<Provider store={store}>
<Routes />
</Provider>,
document.getElementById('root')
);
export default store;

New to react and Redux, why am i getting an undefined store being passed?

I am not sure if I am even setting up this redux-react project correctly. I am confused as to how I can actually start using store within my react app.
When I try to console.log store I am getting undefined. I have gotten most of this from a boilerplate and am unsure of how some of these parts interact. Currently I have an index.js with
import { Provider } from 'react-redux'
import { configureStore } from './store/configureStore';
const store = configureStore()
import { Root} from './containers/Root';
import Home from './containers/Home'
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={Root}>
<IndexRoute component={Home} />
</Route>
</Router>
</Provider>,
document.getElementById('root')
);
Root.js :
import React, { Component } from 'react';
import DevTools from './DevTools';
import MyNavbar from '../components/MyNavbar';
import Footer from '../components/Footer'
module.exports = class Root extends Component {
render() {
const { store } = this.props;
console.log(store)
return (
<div>
<MyNavbar />
{this.props.children}
<Footer />
{/* Being the dev version of our Root component, we include DevTools below */}
{/*<DevTools />*/}
</div>
);
}
};
Home component:
import React, { Component, PropTypes } from 'react';
import { Row, Col, Grid } from 'react-bootstrap'
import HowItWorks from '../components/HowItWorks'
import GetStarted from '../components/GetStarted'
import Setup from './Setup'
export default class Home extends Component {
render() {
// we can use ES6's object destructuring to effectively 'unpack' our props
return (
<section>
<div className="slider-wrapper">
<GetStarted />
</div>
<Grid>
<div className="howwork-wrapper">
<Row >
<Col md={12}>
<HowItWorks />
</Col>
</Row>
</div>
</Grid>
</section>
);
}
}
configureStore.js :
import { createStore, applyMiddleware, compose } from 'redux';
import rootReducer from '../reducers';
import createLogger from 'redux-logger';
import thunk from 'redux-thunk';
import DevTools from '../containers/DevTools';
const logger = createLogger();
const finalCreateStore = compose(
applyMiddleware(logger, thunk),
DevTools.instrument()
)(createStore);
module.exports = function configureStore(initialState) {
const store = finalCreateStore(rootReducer, initialState);
if (module.hot) {
module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers'))
);
}
return store;
};
reducers/index.js:
import { combineReducers } from 'redux';
import auth from './auth'
const rootReducer = combineReducers({
auth
});
export default rootReducer;
reducers/auth.js:
import { LOGIN, LOGIN_FAIL, LOGOUT } from '../constants/ActionTypes'
export default function auth(state = {}, action) {
switch (action.type) {
case LOGIN:
return state;
case LOGIN_FAIL:
return state ;
case LOGOUT:
return state ;
default:
return state;
}
}
constants/ActionTypes:
export const LOGIN = 'LOGIN';
export const LOGIN_FAIL = 'LOGIN_FAIL';
export const LOGOUT = 'LOGOUT';
You need to connect your components to get access to the store/state. To do this, modify your Root component like this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import DevTools from './DevTools';
import MyNavbar from '../components/MyNavbar';
import Footer from '../components/Footer'
class Root extends Component {
render() {
const { state } = this.props;
console.log(state)
return (
<div>
<MyNavbar />
{this.props.children}
<Footer />
{/* Being the dev version of our Root component, we include DevTools below */}
{/*<DevTools />*/}
</div>
);
}
};
const mapStateToProps = (state) => {
return {
state: state
}
}
module.exports = connect(mapStateToProps)(Root);
A few notes, since you are transpiling anyway, you could export instead of module.exports in your declaration. Also, generally you do not want to expose your entire state to a single component. You can connect multiple components (make them "containers") by following this pattern.
The following is an example component connected to your state.
import React, { Component } from 'react';
import { connect } from 'react-redux';
export class SomeComponent extends Component {
render() {
const { someKey, dispatchSomething } = this.props;
return (
<div onClick={dispatchSomething}>
<h1>My rendered someKey variable: {someKey}</h1>
</div>
);
}
};
const mapStateToProps = (state) => {
return {
someKey: state.someReducer.someKey
}
}
const mapDispatchToProps = (dispatch) => {
return {
dispatchSomething: () => dispatch(someActionCreator())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SomeComponent);
References
react-redux API: connect

Resources