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.
Related
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';
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;
}
}
This is weird one for me. I start my app up locally. I just have a Header and TaskPage in my App like so.
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import Header from '../components/header';
import TasksPage from '../pages/tasks';
import { Route } from 'react-router';
class App extends Component {
render() {
return (
<div>
<Header />
<main>
<Route exact path="/" component={TasksPage} />
</main>
</div>
);
}
}
export default withRouter(connect()(App));
And then my router stuff.
import './views/styles/styles.css';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import history from './history';
import configureStore from './store';
import registerServiceWorker from './utils/register-service-worker';
import App from './views/app';
import TaskPage from './views/pages/tasks'
const store = configureStore();
const rootElement = document.getElementById('root');
function render(Component) {
console.log('COMPONENT::',Component);
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<Component />
</div>
</ConnectedRouter>
</Provider>,
rootElement
);
}
if (module.hot) {
module.hot.accept('./views/app', () => {
render(require('./views/app').default);
})
}
registerServiceWorker();
I initially get a blank page. But if I make a change to the App page and save it while the app is running, like just a space or line return, something insignificant, I get my tasks page suddenly. It works fine then. I have no errors in my console at any point during this process so I think I have something wrong with my routing somewhere when the app initially loads.
Or maybe it has something to do with this TaskPage.
import React, { Component } from 'react';
import { List } from 'immutable';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { createSelector } from 'reselect';
import { getTaskFilter, getVisibleTasks, tasksActions } from 'src/tasks';
import TaskFilters from '../../components/task-filters';
import TaskForm from '../../components/task-form';
import TaskList from '../../components/task-list';
export class TasksPage extends Component {
static propTypes = {
createTask: PropTypes.func.isRequired,
filterTasks: PropTypes.func.isRequired,
filterType: PropTypes.string.isRequired,
loadTasks: PropTypes.func.isRequired,
location: PropTypes.object.isRequired,
removeTask: PropTypes.func.isRequired,
tasks: PropTypes.instanceOf(List).isRequired,
updateTask: PropTypes.func.isRequired
};
componentWillMount() {
this.props.loadTasks();
this.props.filterTasks(
this.getFilterParam(this.props.location.search)
);
}
componentWillReceiveProps(nextProps) {
if (nextProps.location.search !== this.props.location.search) {
this.props.filterTasks(
this.getFilterParam(nextProps.location.search)
);
}
}
componentWillUnmount() {
// this.props.unloadTasks();
}
getFilterParam(search) {
const params = new URLSearchParams(search);
return params.get('filter');
}
render() {
return (
<div className="g-row">
<div className="g-col">
<TaskForm handleSubmit={this.props.createTask} />
</div>
<div className="g-col">
<TaskFilters filter={this.props.filterType} />
<TaskList
removeTask={this.props.removeTask}
tasks={this.props.tasks}
updateTask={this.props.updateTask}
/>
</div>
</div>
);
}
}
//=====================================
// CONNECT
//-------------------------------------
const mapStateToProps = createSelector(
getTaskFilter,
getVisibleTasks,
(filterType, tasks) => ({
filterType,
tasks
})
);
const mapDispatchToProps = Object.assign(
{},
tasksActions
);
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(TasksPage));
So something with the hotloading or maybe the way I had it setup?? Im not saying its the best answer but it did resolve my problem. If someone can explain this better than I'll happily accept a clearer response on the subject
This should have worked based on the docs. But I commented it out and went back to just using ReactDOM.Render with my . No wrapped Function or . And oddly hotloading still seems to work.
// function render(Component) {
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<App/>
</div>
</ConnectedRouter>
</Provider>,
rootElement
);
// }
// if (module.hot) {
// module.hot.accept('./views/app', () => {
// render(require('./views/app').default);
// })
// }
FYI - Im react-router 4.x, Redux 5.x, React 15.x.
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;
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