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 = () => {
....
}
Related
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.
I am setting up React-Redux to my web application, I have been getting a Could not find "store" in the context of "Connect(Register)".
I figured maybe I would have to pass the store that I created in [[2]] store.js and it worked but I haven't seen it done this way online and when I tried using redux DevTools it kept showing no store found.
I thought the purpose of connect() was to wrap around the component and access the global state in redux via mapStateToProps. If someone could point me in the right direction or possibly explain why I am getting this error would great!
Something things I have tried:
Since I was had a container component LoginContainer.js (which I could access via connect()(LoginContainer) I thought I would able to access the state here and pass down the props to register.js component and then propagate up state changes.
I have been thinking of changing register.js to a functional component because maybe this may be affecting it somehow?
I have been reading about context and how by using <Provider store={store}> at the index.js it should have allowed access to the Redux store via connect(mapState,MapDispach)(Component) however I still cannot distinguish how or when I am losing to context to Provider.
//index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Provider } from "react-redux"
import { BrowserRouter, Route } from "react-router-dom";
import store from "./store";
ReactDOM.render(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>,
document.getElementById("root")
);
// App.js
import React, { Component } from "react";
import {
Route,
Switch
} from "react-router-dom";
import LoginContainer from "./containers/login";
class App extends Component{
render() {
return (
<div className="App">
<header className="App-header">
<Switch>
<Route path='/login' component={LoginContainer} />
</Switch>
</header>
</div>
);
}
}
export default App;
// LoginContainer.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Register from "../componets/Registration/register";
import Login from "../componets/Login/login";
import { withRouter } from "react-router-dom";
import { Input, Button } from "semantic-ui-react";
import { connect } from "react-redux";
import store from "../store";
class LoginContainer extends Component {
constructor(props) {
super(props);
this.state = {
//some local UI states
};
}
render() {
return (
<div>
<div className="ToggleContainer">
<div className={"toggle"}>
<Button.Group>
<Button
onClick={this.registerHandler}
positive={this.state.registerOn}
>
SignUp
</Button>
<Button.Or />
<Button onClick={this.loginHandler} positive={this.state.loginOn}>
Login
</Button>
</Button.Group>
</div>
<Provider store = {store}> <Register {...this.props} /> </Provider> // [[6]], uncommented [[4]] works but is it different than method [[1]]?
// <Register{...this.props} /> // [[2]] cannot connect to store
// <Register store={store} {...this.props} />} [[1]] can connect store via directly sending store //
</div>
</div>
);
}
}
// ReactDOM.render(<LoginContainer />, document.getElementById("root")); [[answer]] removing this fixed the problem
const mapStateToProps = state => {
return {
loggedIn: state.registration.isLoggedIn
};
};
const mapDisptachToProps = dispatch => ({
registerUser: id => dispatch({ type: "SIGN_IN" })
});
export default withRouter(
connect(
mapStateToProps,
mapDisptachToProps
)(LoginContainer)
);
//components/register.js
import React, { Component } from "react";
import { Input, Button } from "semantic-ui-react";
import { connect } from "react-redux";
class Register extends Component {
constructor(props) {
super(props);
// Local State
}
const check() {
let userData: User;
if (validEmail(this.state.email)) {
userData = {
//data
};
let user = await this.handleSignUp(userData);
const res = JSON.parse(user);
if (res.message === "Success") {
alert('Account success')
}
if(typeof user === 'string'){
user =JSON.parse(user)
}
} else {
this.setState({ hiddenErrorMessage: false });
}
this.props.registerUser(userData);
// func: returns true if it is valid, o.w. false
function validEmail(email) {
}
};
//handlers()
render() {
return (
// eslint-disable-next-line react/jsx-filename-extension
<div>
<Input
className="customInput"
onChange={this.handleEmail}
placeholder="Email"
/>
<Input
className="customInput"
onChange={this.handlePassword}
placeholder="Password"
type="password"
/>
<br />
<Button
size="big"
className="customButton"
onClick={this.checkUserInput}
>
Sign up
</Button>
<p>
<span hidden={this.state.hiddenErrorMessage}>
Invalid Email/Passoword
</span>
</p>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loggedIn:state.registration.isLoggedIn
}
};
const mapDisptachToProps = dispatch => ({
registerUser: id => dispatch({ type: "SIGN_IN" }) // return dispatch
});
export default Register // [[3]] Works as expected
// export default connect( [[4]] Could not find "store" in the context of "Connect(Register)". Either wrap the root component in a <Provider> Error
// mapStateToProps,
// mapDisptachToProps
// )(Register);
//store.js
import { createStore } from "redux";
import rootReducer from './reducers';
export default createStore(rootReducer);
//reducers/index.js
import { combineReducers } from "redux";
import registration from "./registration";
export default combineReducers({ registration });
//actionType.js
const SIGN_IN = 'SIGN_IN';
export { SIGN_IN };
//reducers/registration.js
const initialState = {
isAuthenticated: false,
isLoggedIn: false,
isLoggedOut: false,
userId : ''
};
export default (state = initialState, action) => {
if (action.type === "SIGN_IN") {
return { ...state, isLoggedIn: true };
}
if (action.type === "SIGN_OUT") {
return { ...state, isLoggedOut: true };
}
return state;
};
Solution to this problem was removing the next code from the LogainContainer:
ReactDOM.render(<LoginContainer />, document.getElementById("root"));
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;
}
}
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
Going through udemy tutorial and got stuck and for some reason can't figure out what happaned. I went through all my code and it looks right as far as I can tell compared to the tutorial. Code:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';
import App from './components/app';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<App />
</Provider>
, document.querySelector('.container'));
searchbar.js:
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {fetchWeather} from '../actions/index';
export default class SearchBar extends Component{
constructor(props){
super(props);
this.state = {term: ''}
this.onInputChange = this.onInputChange.bind(this)
}
onInputChange(e){
console.log(e.target.value)
this.setState({
term: e.target.value
})
}
onFormSubmit(e){
e.preventDefault()
}
render(){
return (
<form onSubmit ={this.onFormSubmit} className = "input-group">
< input
placeholder =" Get a forecast"
className = "form-control"
value = {this.state.term}
onChange = {this.onInputChange}
/>
<span className = "input-group-btn">
<button type="submit" className = "btn btn-secondary">Submit </button>
</span>
</form>
);
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({fetchWeather}, dispatch);
}
export default connect (null, mapDispatchToProps)(SearchBar);
reducers/index.js
import axios from 'axios';
const API_KEY = 'c4c2ff174cb65bad330f7367cc2a36fa'
const ROOT_URL = `http://api.openweathermap.org/data/2.5/forecast?q=appid=${API_KEY}`;
export const FETCH_WEATHER = 'FETCH_WEATHER';
export function fetchWeather(city){
let url = `${ROOT_URL}&q=${city},us`;
let request = axios.get(url);
return {
type: FETCH_WEATHER,
payload: request
};
}
app.js
import React, { Component } from 'react';
import SearchBar from '../containers/search_bar';
export default class App extends Component {
render() {
return (
<div>
<SearchBar />
</div>
);
}
}
To answer,
Your code got a little mixed up, the block that you have in reducers/index.js is your action and should be located in actions/index.js instead. As mentioned you are importing it from there in your searchbar component:
import {fetchWeather} from '../actions/index';
The reducer here should be making use of the FETCH_WEATHER type that you are setting up in your action in order to update the state of the redux store, so something along the lines of:
switch(action.type) {
case FETCH_WEATHER:
return [action.payload.data].concat(state);
}
return state;
Then either export that directly or make use of combineReducers from redux to return a single reducer function if you have more than one.
Link to the always awesome: DOCS