Redux State in Component - reactjs

trying to figure out how to pull Redux store state into my component from Redux. I've got mapStateToProps and "connect" wired up. However, when I click my button in "App" component, this.props doesn't have my Redux values in it.
// React and Redux Const
const { Component } = React;
const { render } = ReactDOM;
const { Provider, connect } = ReactRedux;
const {createStore, combineReducers, bindActionCreators } = Redux;
function tweetReducer(state=[],action) {
if(action.type === 'ADD_TWEET') {
return state.concat({ id: Date.now(), tweet: action.payload})
} else {
return state;
}
}
const rootReducer = combineReducers ({
state: (state = {}) => state,
tweets: tweetReducer
});
class App extends Component{
buttonClicked() {
store.dispatch({type: 'ADD_TWEET', payload: 'This is my first
tweet!'});
console.log(this.props)
}
render() {
return (
<div>
<h5>Hello from App</h5>
<button onClick={this.buttonClicked.bind(this)}>Button</button>
<div>-------------------</div>
<Display />
</div>
)
}
}
class Display extends Component {
render() {
return (
<div>
<h3>Tweets:</h3>
{this.props.tweets}
</div>
)
}
}
function mapStateToProps(state) {
console.log('mapping state to props')
return {
tweets: state.tweets
}
}
let store = createStore(rootReducer)
render (
<Provider store={store}>
<App />
</Provider>
, document.querySelector('#app')
);
connect(mapStateToProps)(App)
console.log(store.getState());

Looks like you've got a couple issues there.
First, it helps to understand that connect()(MyComponent) returns a new component that "wraps" around your "real" component. In your example, you're calling connect() after you've rendered <App />, and you aren't actually saving and using the component generated by connect(). What you need is something like:
let store = createStore(rootReducer)
const ConnectedApp = connect(mapStateToProps)(App);
render(
<Provider store={store}>
<ConnectedApp />
</Provider>
, document.querySelector('#app')
);
Second, part of the point of connecting a component is that it shouldn't actually reference the store directly. connect() can take a second parameter, known as mapDispatchToProps. If you don't supply a mapDispatch parameter, it will automatically give your component this.props.dispatch. So, your App component should look like this to start with:
class App extends Component{
buttonClicked(){
this.props.dispatch({type: 'ADD_TWEET', payload: 'This is my first tweet!'});
console.log(this.props)
}
That should be enough to get the App component receiving data from Redux and dispatching actions.

This is how you should proceed with getting store state in your component.
1) Create reducers folder and create a new file index.js
/* reducers/index.js */
import { combineReducers } from "redux";
import tweetReducer from "./tweetReducer";
const rootReducer = combineReducers({
tweets: tweetReducer
});
export default rootReducer;
/* reducers/tweetReducer.js */
function tweetReducer(state=[],action) {
switch(action.type) {
case 'ADD_TWEET':
return state.concat({ id: Date.now(), tweet: action.payload});
default:
return state;
}
}
/* components/App.js */
import React, { Component } from 'react';
import { connect } from 'react-redux';
class App extends Component {
buttonClicked() {
this.props.store.dispatch({type: 'ADD_TWEET', payload: 'This is my first
tweet!'});
console.log(this.props)
}
render() {
return (
<div>
<h5>Hello from App</h5>
<button onClick={this.buttonClicked.bind(this)}>Button</button>
<div>-------------------</div>
<Display />
</div>
)
}
}
function mapStateToProps(state) {
console.log('mapping state to props')
return {
tweets: state.tweets
}
}
export default connect(mapStateToProps)(App);
/* components/Display.js */
import React, { Component } from 'react';
export default class Display extends Component {
render() {
return (
<div>
<h3>Tweets:</h3>
{this.props.tweets}
</div>
)
}
}
/*Main.js */
import React, { Component } from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import { store } from "./store";
render(
<Provider store={store}>
<App store={store} />
</Provider>,
document.querySelector('#app')
);
/* store/index.js */
import { createStore, applyMiddleware, compose } from "redux";
import reducers from "../reducers";
const store = createStore(
reducers,
composeEnhancers(applyMiddleware())
);
export { store };

Related

State is empty when using mapStateToProps

The state is empty when I try to map the state from the store to the properties of a component. I try to get a value of a string displayed in JSX but it is missing. I cant manage to get anything to display from the redux store.
Reducer:
const initialState = {
visibles: "false",
error: null,
text: ""
};
const rootReducer = (
state = initialState,
action
) => {
switch (action.type) {
case "OPEN_MODAL":
return {
...state,
visibles: "true",
error: null
};
default:
return state;
}
}
export default rootReducer;
and index.js
import {createStore } from "redux";
import {Provider } from "react-redux";
import rootReducer from "./components/Redux/Reducer";
const store = createStore(rootReducer);
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
consumer of the redux store
import React, { Component } from 'react'
import {connect} from "react-redux";
import styles from "./modal.module.css";
export class Modal extends Component {
render() {
console.log(this.props)
return (
<div className={styles.root}>
<p className={styles.title}>{this.props.visible}</p>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
visible: state.visibles
}
}
export default connect(mapStateToProps)(Modal)
Found the reason why. I had to refactor the Modal class to not use "export class" and then I could get the state from the store with connect.
class Modal extends React.Component {
render() {
console.log(this.props)
return (
<div className={styles.root}>
<p className={styles.title}>{this.props.visible}</p>
</div>
)
}}

Redux don't render the content of store

I'm trying to render the store's value on view, but it don't render. I don't know why it is not rendering. Can someone explain me why ? (I have watched Dan Abramov's course on Egg Head and I still not understanding)
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
const reducer = (state = 0, action) => {
switch(action.type) {
case 'INCREMENT':
console.log('Increment', state);
return ++state;
case 'DECREMENT':
console.log('Decrement', state);
return --state;
default:
return state;
}
}
const store = createStore(
reducer
)
class App extends Component {
render() {
return (
<div>
<p>{JSON.stringify(this.props.store)}</p>
<button onClick={() => store.dispatch({type: 'INCREMENT'})}>+</button>
<button onClick={() => store.dispatch({type: 'DECREMENT'})}>-</button>
</div>
);
}
}
ReactDOM.render(
<App store={store} />,
document.getElementById('root')
);
Use Redux Connect to access state in component
ex:
export default connect(mapStateToProps, actions)(yourComponent);
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
const reducer = (state = 0, action) => {
switch(action.type) {
case 'INCREMENT':
console.log('Increment', state);
return ++state;
case 'DECREMENT':
console.log('Decrement', state);
return --state;
default:
return state;
}
}
const store = createStore(
reducer
);
class App extends Component {
update() {
this.forceUpdate();
}
componentDidMount(){
this.unsubscribe = store.subscribe(this.update.bind(this));
}
componentWillUnmount(){
this.unsubscribe();
}
render() {
return (
<div>
<p>{JSON.stringify(store.getState())}</p>
<button onClick={() => store.dispatch({type: 'INCREMENT'})}>+</button>
<button onClick={() => store.dispatch({type: 'DECREMENT'})}>-</button>
</div>
);
}
}
export default App;
check this
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reduxThunk from 'redux-thunk';
import reducers from './reducers';
const store = createStore(
reducers,
applyMiddleware(reduxThunk)// not needed
);
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.querySelector('#root')
);
App component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
class Signout extends Component {
componentDidMount() {
console.log(this.props.data);
}
render() {
return <div>Sorry to see you go</div>;
}
}
function mapStateToProps(state) {
return { data: state.data};
}
export default connect(mapStateToProps, actions)(App);
You shouldn't mutate the state directly. Instead do like this:
return state + 1; // instead of ++state
return state - 1; // instead of --state
To render the state:
class App extends Component {
render() {
return (
<div>
<p>{store.getState()}</p>
To get the changes of the store, you will also need to subscribe for the changes:
const unsubscribe = store.subscribe( // generally, this is called on event handler
ReactDOM.render(
<App store={store} />,
document.getElementById('root')
)
)
unsubscribe()
You may also subscribe the changes in componentDidMount and unsubscribe in the componentWillUnmount lifecycle hook. You may look at the answer provided by henok tesfaye: subscribing and unsubscribing in lifecycle.
You can read it from the docs to know more about it.
But in general, we don't need subscriber. We can get the changes through mapping the states using connect. This blog explains it very well. So, I would suggest you to look at Usage with react

Could not find store in either the context or props of connect site redux error

I am trying to create something similar to todo app with react and redux from here.I have been reading all the solutions for this problem and nothing seems to be working for my case.
Most of the solutions purpose using Provider which I am already using. Any help is much appreciated.
Edit - Few import statement might be missing in snippets, but all components are being imported whenever needed and actions are also defined in action file.
Index.js
import App from './components/App'
import reducer from './reducers/index'
const store = createStore(reducer)
const AppWithStore = (
<Provider store={store}>
<App />
</Provider>
)
ReactDOM.render(AppWithStore, document.getElementById('root'))
Update - Combined Reducer code
import React from 'react'
import { combineReducers } from 'redux'
import TestReducer from './TestReducer'
export default combineReducers({
TestReducer,
})
App.js
import Test from './Test';
class App extends Component {
render() {
return (
<Test />
);}
}
ReactDOM.render(
(<App/>),
document.getElementById("root")
);
export default App
Test.js contains both component and container
import { connect } from 'react-redux'
import { add } from '../actions'
const mapStateToProps = state => ({
todos: state.todos,
})
class Test extends Component {
dosomething() {
const dispatch = this.props;
dispatch(add("New Note"));
}
render() {
return (
<div>
< button OnClick = { this.dosomething.bind(this)} > Test </button>
</div>
)
}
}
export default connect(mapStateToProps)(Test)
The reducer logic for Test is as given below
import React from 'react';
const TestReducer = (state = [], action) => {
const todos = state;
const {type, payload} = action;
switch(action.type){
case 'ADD': {
return {
...state,
todos:"new todo"
}
}
}
return state;
}
export default TestReducer
You should remove
ReactDOM.render(
(<App/>),
document.getElementById("root")
); from App.js file
When you call it again in App.js a new component instance independent of others is created, That's why it is not finding store.As store is not passed to it.
You can check it here https://codesandbox.io/s/vy7wwqw570. As i had remove render api call from app.js it is working now.
you need to import your reducers so that you
if your file name is TestReducer.js
import React from 'react';
export const TestReducer = (state = [], action) => {
const todos = state;
const {type, payload} = action;
switch(action.type){
case 'ADD': {
return {
...state,
todos:"new todo"
}
}
}
return state;
}
then import it in this manner
import {TestReducer} from './TestReducer.js';
const store = createStore(TestReducer);
Try replacing todos: state.todos with todos: state.TestReducer.todos
import { connect } from 'react-redux'
import { add } from '../actions'
const mapStateToProps = state => ({
//todos: state.todos,
todos: state.TestReducer.todos
})
class Test extends Component {
dosomething() {
const dispatch = this.props;
dispatch(add("New Note"));
}
render() {
return (
<div>
< button OnClick = { this.dosomething.bind(this)} > Test </button>
</div>
)
}
}
export default connect(mapStateToProps)(Test)

Unable to trigger an update with Redux

I am new to redux and I am trying to build a simple Hello World to try out this library. However, I am having trouble with getting the value in the Home component. The two buttons should trigger two different changes. I think the errors must have something to do with the connect method. After hours of research, I still cannot figure out why it does not work. Thank you in advance.
Below is my code:
Home.js -> component
import React from "react";
import { connect } from "react-redux";
import * as actionCreators from "../actions/display.js";
import { bindActionCreators } from "redux";
const Home = props => {
return (
<div>
Message:
<h1>{props.message}</h1>
<button onClick={props.sayHi}>SayHI</button>
<button onClick={props.sayHello}>Say Hello</button>
</div>
);
};
function mapStateToProps(state) {
return { ...state };
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
...actionCreators
},
dispatch
);
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
App.js
import React from "react";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import createHistory from "history/createBrowserHistory";
import { Route } from "react-router";
import {
ConnectedRouter,
routerReducer,
routerMiddleware
} from "react-router-redux";
import Home from "./components/Home";
import reducers from "./reducers/reducer"; // Or wherever you keep your reducers
// Create a history of your choosing (we're using a browser history in this case)
const history = createHistory();
// Build the middleware for intercepting and dispatching navigation actions
const middleware = routerMiddleware(history);
// Add the reducer to your store on the `router` key
// Also apply our middleware for navigating
const store = createStore(
combineReducers({
...reducers,
router: routerReducer
}),
applyMiddleware(middleware)
);
const App = () => (
<Provider store={store}>
{/* ConnectedRouter will use the store from Provider automatically */}
<ConnectedRouter history={history}>
<Route path="/" component={Home} />
</ConnectedRouter>
</Provider>
);
export default App;
reducer.js
import { SAY_HELLO, SAY_HI } from "../constants";
const initialState = {
message: "Mark"
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case SAY_HELLO:
return { ...state, message: "Hello Mark" };
case SAY_HI:
return { ...state, message: "Hi Mark" };
default:
return state;
}
};
export default reducer;
actions/display.js
import { SAY_HELLO, SAY_HI } from "../constants";
export const sayHello = () => ({
type: SAY_HELLO
});
export const sayHi = () => ({
type: SAY_HI
});
constants.js
export const SAY_HELLO = "SAY_HELLO";
export const SAY_HI = "SAY_HI";
Update:
I figured a working solution for my code but not an ideal one. I change state=>({message:state.message}) to state=>state which means now my component subscrubes to the global state. I also change{props.message} to {props.defaultmessage} in the hi tag on Home.js. Below is the updated code.
import React from "react";
import { connect } from "react-redux";
import { sayHello, sayHi } from "../actions/display.js";
const Home = props => {
return (
<div>
Message:
{console.log(props.default.message)}
<h1>{props.default.message}</h1>
<button onClick={props.sayHi}>SayHI</button>
<button onClick={props.sayHello}>Say Hello</button>
</div>
);
};
export default connect(state => state, {
sayHello,
sayHi
})(Home);
The problem is in that part of your code:
const store = createStore(
combineReducers({
...reducers,
router: routerReducer
}),
applyMiddleware(middleware)
);
reducers variable contains reducer function, but you are using it as object here.
You should assign your reducer with a specific key in the state, for example data:
const store = createStore(
combineReducers({
data: reducers,
router: routerReducer
}),
applyMiddleware(middleware)
);
Next, message value will be available at state.data path:
function mapStateToProps(state) {
return { message: state.data.message };
}
Hoop it work!
import React from "react";
import { connect } from "react-redux";
import { sayHi, sayHello } from "../actions/display.js";
const Home = props => {
return (
<div>
Message:
<h1>{props.message}</h1>
<button onClick={props.sayHi}>SayHI</button>
<button onClick={props.sayHello}>Say Hello</button>
</div>
);
};
function mapStateToProps(state) {
return { message: state.message };
}
export default connect(mapStateToProps, { sayHi, sayHello })(Home);

Unable to implement Redux store in React Native

I'm trying hard to wire redux store in a react-native app but seems like I'm missing something. Any help will be appreciated.
action.js
export const getdata = (data) => {
return (dispatch, getState) => {
dispatch({
type: "GET_DATA",
data
});
};
};
reducer/dataReducer.js
export default (state = {}, action) => {
switch (action.type) {
case GET_DATA:
return { ...state, response: action.data };
default:
return state;
}
};
reducer/index.js
import { combineReducers } from 'redux';
import dataReducer from './dataReducer';
//other imports
export default combineReducers({
data: dataReducer,
//other reducers
});
store/configureStore.js
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from './../reducer;
export default function configure(initialState = {}) {
const store = createStore(reducer, initialState, compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f
));
return store;
}
main.js (where I dispatch action)
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import Routes from './Routes';
import configureStore from './store/configureStore';
import { getdata} from './actions';
const store = configureStore();
store.subscribe(() => {
console.log('New state', store.getState); //doesn't update at all
});
class Main extends Component {
componentDidMount() {
store.dispatch(getdata('abc')); //calling action creator
}
render() {
return (
<Provider store={store}>
<Routes />
</Provider>
);
}
}
export default Main;
I also tried wiring Chrome extension to see redux store updates, but no luck there. It always says no store found. How can I get this working?
store can be accessed in the a child class inside the Routes Component by react-redux connect
but here no store in the class but I think You can do the following
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import Routes from './Routes';
import configureStore from './store/configureStore';
import { getdata} from './actions';
const store = configureStore();
store.subscribe(() => {
console.log('New state', store.getState()); //getState() is method
});
store.dispatch(getdata('abc'));
class Main extends Component {
render() {
return (
<Provider store={store}>
<Routes />
</Provider>
);
}
}
export default Main;
You want to dispatch your actions from your container components (aka. smart components, the ones connected to the Redux store). The container components can define props in mapDispatchToProps that let them dispatch actions. I don't have your code, so I'm just gonna assume that your Routes component is the container component that you are connecting to the Redux store. Try something like:
class Routes extends Component {
....
componentDidMount() {
this.props.retrieveData();
}
...
}
const mapStateToProps = state => {
...
};
const mapDispatchToProps = dispatch => {
return {
retrieveData: () => dispatch(getdata('abc'));
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Routes);

Resources