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
Related
I am new to Redux and was learning how to use it with React. Basically, I did everything correctlyin terms of setting up Redux with react app but when I click on button increment I expect displaying counter to increment by one. But when I do that nothing happens and, certainly, I have checked dispatch and action being sent but basically all is ok. Thus I truly need your help guys here is the code I am using:
index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";
import { createStore } from "redux";
import reducer from "./store/reducer";
import { Provider } from "react-redux";
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
registerServiceWorker();
reducer.js
const initialState = {
counter: 0
};
const reducer = (state = initialState, action) => {
if (action.type === "INCREMENT") {
return { counter: state.counter + 1 };
}
return state;
};
export default reducer;
counter.js
import React, { Component } from "react";
import { connect } from "react-redux";
import CounterControl from "../../components/CounterControl/CounterControl";
import CounterOutput from "../../components/CounterOutput/CounterOutput";
class Counter extends Component {
render() {
return (
<div>
<CounterOutput value={this.props.ctr} />
<CounterControl
label="Increment"
clicked={() => this.props.onIncrement}
/>
</div>
);
}
}
const mapStateToProps = state => {
return { ctr: state.counter };
};
const mapDispatchToProps = dispatch => {
return {
onIncrement: () => dispatch({ type: "INCREMENT" })
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
Did you register your reducer with store?
If not, please do that.
I believe this thing should not return string:
const mapStateToProps = state => {
return { ctr: state.counter };
};
I have a functional counter app I'm working on with react-redux. I can see every time that I dispatch an action using my Increment/Decrement functions the state updates, however, I am unable to actually output the value of my state.
This is my first time trying to build a redux application with modules(as opposed to building an html file with a redux cdn). Please help me figure out what I'm doing wrong.
Thanks.
//App.js
import { Increment, Decrement } from './redux'
class App extends Component {
render() {
return (
<div className="App">
<h1>{this.props.count}</h1> //Doesn't output anything
<button onClick={this.props.Increment}>+</button>
<button onClick={this.props.Decrement}>-</button>
</div>
);
}
}
const mapStateToProps = state => ({
count: state.count
})
const mapDispatchToProps = {
Increment,
Decrement
}
const AppContainer = connect(
mapStateToProps,
mapDispatchToProps
)(App)
export default AppContainer
//redux.js
import { createStore } from 'redux';
export const Increment = () => {
return {
type: 'INCREMENT'
}
}
export const Decrement = () => {
return {
type: 'DECREMENT'
}
}
export const reducer = (state = 0, action) => {
switch(action.type){
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
export function configureStore(intitalState = 0){
const store = createStore(reducer, intitalState)
return store;
}
export const store = configureStore()
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import { store } from './redux';
import AppContainer from './App';
ReactDOM.render
(<Provider store={store}>
<AppContainer/>
</Provider>, document.getElementById('root'));
In this case state is actually a value, not an object that stores value as count property.
It should be either:
const mapStateToProps = state => ({
count: state
})
Or combineReducers can be used to map specific reducer as store property:
const store = createStore(combineReducers({count: reducer }), intitalState)
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)
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);
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 };