React-redux connect() fails to pass the props - javascript

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.

Related

is there solve for this Error: Could not find "store" in the context of "Connect(App)". Either wrap the root component in a <Provider>

when I launch with code i faced this problem
Error: Could not find "store" in the context of "Connect(App)". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(App) in connect options.
my code
app.js
'''
import React, { Component } from 'react';
import {connect } from 'react-redux'
class App extends Component {
increase = ()=>{
this.setState({
count: this.state.count + 1
})
}
decrease = ()=>{
this.setState({
count :this.state.count - 1
})
}
render(){
return (
<div>
<button onClick={this.increase}>+</button>
<div> {this.props.count}</div>
<button onClick={this.decrease}>-</button>
</div>
);
}
}
const mapStateToProps = state =>{
return{
count:state.count
}
}
export default connect(mapStateToProps)(App);
'''
rootReducer.js
'''
const initState ={
count : 0
}
const reducer = (state = initState , action)=>{
return state;
}
export default reducer;
rootReducer.js
const initState ={
count : 0
}
const reducer = (state = initState , action)=>{
return state;
}
export default reducer;
i change it but still display for me that message, I created store file and put in it this code
import {createStore} from 'redux';
import {reducer , initialState } from './reducer';
export const ConfigureStore =()=>
{ const store = createStore( reducer, initialState );
return store
}
and put provider in app but still display message error any other solution for this problem?
Yes. As explained in error message. You should wrap your root component of app with Provider Component from react-redux.
It should look like this
import { Provider } from 'react-redux';
import store from './store';
export default function App(){
return (
<Provider store={store}>
<App/>
<Provider />);
}
./store.js should have store created with reducers and middlerwares(if any).

React-Redux connect isn't getting store from Provider

Im using Redux with React Native to manage state. I believe that I've successfully set up the store and Provider. I can use store.getState() and store.dispatch(action()) from any component successfully, however, the react-redux connect function is not allowing me to access the store from child components. Can you find anything wrong with my code below?
Login.js - This child component I'm testing won't access redux store with react-redux connect.
import React, {Component} from 'react';
import actions from '../../redux/actions';
import {connect} from 'react-redux';
const mapStateToProps = state => {
// To test if this function fires, which it is not
console.log('login state mapping through redux');
return {
state: state,
};
};
const dispatchToProps = dispatch => {
return {
userRecieved: (user) => dispatch(actions.userRecieved(user)),
};
};
export class Login extends Component {
constructor(){
super();
this.state = {
credentials: {
email: '',
password: '',
},
};
}
componentDidMount(){
// This will show whether redux is connected
console.log(this.props.state);
this.props.userRecieved('TEST USER');
}
render() {
return ( <Text>{this.props.state}</Text> );
}
}
export default connect(mapStateToProps, dispatchToProps)(Login);
App.js
import React, {Component} from 'react';
import YEET from './src/YEET.js';
import store from './src/redux/stores/index';
import {Provider} from 'react-redux';
export default class App extends Component {
render() {
return (
<Provider store={store}>
<YEET />
</Provider>
);
}
}
My Redux Files:
store.js
import { combineReducers, createStore} from 'redux';
import accountReducer from '../reducers/accountReducer';
import postReducer from '../reducers/postReducer';
const initialState = {};
const reducers = combineReducers({
account: accountReducer,
post: postReducer,
});
const store = createStore(reducers, initialState);
export default store;
actions.js
import constants from '../constants';
var userRecieved = user => ({
type: constants.USER_RECIEVED,
data: user,
});
export default {
userRecieved,
};
accountReducer.js
import constants from '../constants';
var initialState = {
user: {
photos: [],
},
};
export default (state = initialState, action ) => {
let newState = Object.assign({}, state);
switch (action.type) {
case constants.USER_RECIEVED:
const user = {
id: action.data.uid,
// photos: action.data,
};
console.log(action);
newState.user = user;
return newState;
default:
return state;
}
};
From what I see, the only reason could be that you're importing the unconnected component.
When you import the Login component, make sure that you import the default export instead of the named export.
So, wherever you import the Login component, do it like this:
import Login from 'your-login-component-location/Login'
instead of
import { Login } from 'your-login-component-location/Login'
The second one is a named export, which will return the Login class directly.
The first one is the default export, which will return the connected component.

When accessing props of a component "this" is unedefined after mapping state to props

While using react-redux and mapping state to props with connect() the received state has the correct values but when accessing the variables through this.props "this" is always undefined.
When pressing the button (in there for test purposes only) on the sidebar component the second console log always fails with TypeError: Cannot read property 'props' of undefined on console.log(this.props.visibility).
Am i missing something extra that is needed in order to allow access of the newly maped values in mapStateToProps through this.props ? or even if i should be accessing them in another way other then this.props.
Thanks in advance.
The component
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {toggleSidebarVisibility } from '../../actions/layoutActions';
class Sidebar extends Component{
render(){
return(
<div>
<button class='btn btn-secondary' onClick={test}>B</button>
</div>
);
}
}
function test(){
console.log("hello");
console.log(this.props.visibility)
}
const mapStateToProps = (state) => {
console.log(state);
console.log(state.layout.sidebarVisibility);
return {
visibility: state.layout.sidebarVisibility,
}
};
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import { Provider } from 'react-redux';
import Sidebar from './components/layout/Sidebar';
import Header from './components/layout/Header';
import store from './store';
class App extends Component {
render() {
return (
<Provider store ={store}>
<div className="App">
<Header />
<Sidebar/>
</div>
</Provider>
);
}
}
export default App;
Store.js
import { createStore, applyMiddleware,compose} from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {
};
const middleWare = [thunk];
const store = createStore(rootReducer,initialState,compose(
applyMiddleware(...middleWare),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
));
export default store;
RootReducer
import {combineReducers } from 'redux';
import layoutReducer from './layoutReducer';
export default combineReducers({
layout : layoutReducer
});
LayoutReduccer
import {TOGGLE_SIDEBAR_VIS} from '../actions/types';
const initialState = {
sidebarVisibility : true
}
export default function(state = initialState,action){
switch(action.type){
case TOGGLE_SIDEBAR_VIS:
return{ ...state,sidebarVisibility:action.payload};
default:
return state;
}
}
When you write functions inside class/statefull component they no need function keyword before function name. So test function can be written as test(){ or arrow function test = () => {
The reason this is undefined inside function in your case because function needs binding in order to access this and state or props. Either you need to manually bind the function in constructor or change it to arrow function like I did below
Approach 1:
Manually bind the function in constructor
constructor(props){
super(props);
this.test = this.test.bind(this);
}
test = () => {
console.log("hello");
console.log(this.props.visibility);
}
OR
Approach2:
You can change test function to arrow function hence manual binding in constructor isn’t required because arrow functions takes binding automatically thus this is available inside arrow function
Change
function test(){
console.log("hello");
console.log(this.props.visibility);
}
To
test = () => {
console.log("hello");
console.log(this.props.visibility);
}
This is related to JavaScript#lexical scoping.
One need to bind to component this using .bind or JavaScript#arrow function.
Sol1 : Using JavaScript#arrow function :
test = () => {
console.log("hello");
console.log(this.props.visibility)
}
Sol2: Using explicit this binding using JavaScript#bind.
inside constructor:
constructor(){
this.test = this.test.bind(this);
}
test(){
//code
}

Why doesn't action fire in my react\redux app?

Currently, I'm working on a small application that utilizes modals. I don't want to use 'ready-to-use' packages like react-modal and instead decided to try to do it on my own.
1) A reducer in src/reducers/modalReducer.js
const modalReducer = (state = {
show: true,
}, action) => {
switch (action.type) {
case 'TOGGLE_MODAL':
console.log('reducer worked out')
state = {...state, show: !state.show }
break
default:
return state
}
}
export default modalReducer
My reducers/index.js
import { combineReducers } from 'redux'
import modalReducer from './modalReducer'
const reducers = combineReducers({
modal: modalReducer
})
export default reducers
2) A store in src/store.js
import { createStore } from 'redux'
import reducer from './reducers/index'
export default createStore(reducer)
3) A Modal component in src/components/Modal.js. I want this component to be reusable and contain input forms which I'll add later.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { toggleModal } from '../actions/index'
import { bindActionCreators } from 'redux'
import '../css/Modal.css'
class Modal extends Component {
render () {
if(!this.props.show) {
return (<h1>FUC YOU</h1>)
}
console.log('HELLLO' + this.props.show)
return (
<div className='backdrop'>
<div className='my-modal'>
<div className='footer'>
<button className='close-btn' onClick={ () => toggleModal }>
X
</button>
</div>
<h1>{ this.props.title }</h1>
<hr/>
<div>
{ this.props.contents }
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return { show: state.modal.show }
}
const mapDispatchToProps = (dispatch) => {
return {
toggleModal: () => dispatch(toggleModal())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Modal)
My problem is that when I'm pressing the button x, in my modal nothing happens. It means that I did something wrong when was dispatching actions, but I have no idea what I missed...
At this point I just want my empty modal to be closed when the x button is pressed.
In my index.js I have the following structure:
import React from 'react'
import ReactDOM from 'react-dom'
import registerServiceWorker from './registerServiceWorker'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import store from './store.js'
import App from './components/App'
ReactDOM.render(
<Provider store = {store} >
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'))
registerServiceWorker()
My Modal component is within App
You're not actually calling the toggleModal() action creator. In addition, you're referencing the imported function, not the function you're getting as props:
onClick={ () => toggleModal }
The immediate fix would be: onClick={ () => this.props.toggleModal() }.
Having said that, there's two other ways you can improve this code.
First, you can pass toggleModal directly as the handler for onClick, like:
onClick={this.props.toggleModal}
Second, you can replace the mapDispatch function by using the "object shorthand" syntax supported by connect:
import {toggleModal} from "../actions";
const actions = {toggleModal};
export default connect(mapState, actions)(Modal);
Beyond that, I'd encourage you to read my post Practical Redux, Part 10: Managing Modals and Context Menus, which specifically shows how to implement modal dialogs using React and Redux, and points to additional resources on the topic.

Print value from props, which is delivered to the component from redux by mapStateToProps

Problem:
I can't display the value from the state of redux, which is delivered by mapStateToProps function to the component.
Project structure:
Create-react-app CLi application built the project.
Inside of the src/ I have the following code structure
Necessary code:
The main page which we are interacting with looks like this:
Underneath it is planned to post the result of the clicking on the buttons.
So how do I bind the redux state and actions to those two components: Calculator and ResultLine?
Let me show the index.js code, where I create the store:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from './reducers/';
import App from './components/App';
ReactDOM.render(
<Provider store={createStore(reducers)}>
<App />
</Provider>,
document.getElementById("root")
);
There are only three actions:
import {CALCULATE, ERASE, PUT_SYMBOL} from "./types";
export const putSymbol = (symbol) => {
return {
type: PUT_SYMBOL,
payload: symbol
}
};
export const calculate = () => {
return {
type: CALCULATE
}
};
export const erase = () => {
return {
type: ERASE
}
};
And in the App.js I pass reducers, which are binded to those actions to the Calculator component:
import React, {Component} from 'react';
import Calculator from './Calculator';
import ResultLine from "./ResultLine";
import {calculate, erase, putSymbol} from "../actions/index";
import {connect} from "react-redux";
class App extends Component {
render() {
return (
<div>
<Calculator
onSymbolClick={this.props.onSymbolClick}
onEqualsClick={this.props.onEqualsClick}
onEraseClick={this.props.onEraseClick}/>
<br/>
<ResultLine result={this.props.result}/>
</div>
);
}
}
const mapStateToProps = (state) => {
console.log('mapState', state.calc.line);
return {
result: state.line
}
};
const mapDispatchToProps = {
onSymbolClick: putSymbol,
onEqualsClick: calculate,
onEraseClick: erase
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
And that works fine. Whenever I click the button the state changes, and I observe it in the console log, called in mapStateToProps function.
So I expect, that I can deliver result prop to the Result line easily, and I pass it into the ResultLine component as a parameter. So, let's look at that element:
import React from 'react';
const ResultLine = ({result}) => {
return (
<p>{result}</p>
);
};
export default ResultLine;
And I can see no changes in a result line. Maybe, something wrong with the React/Redux lifecycle management and ResultLine component just does not update on changes in state?
There's an error on mapStateToProps.
Instead of:
const mapStateToProps = (state) => {
return {
result: state.line
}
}
Please use:
const mapStateToProps = (state) => {
return {
result: state.calc.line // calc was missing here
}
}

Categories