Using Redux without React to export functions - javascript

I want to be able to create a list of functions that are connected to Redux without using any React so that I can use these functions in various components across my app. How can I do this without using React / react components? Please see the below for my approach. Am I able to use react-redux's connect? Thanks.
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {fetchUsers} from './actions';
class Sample {
getUsers() {
this.props.fetchUsers();
}
displayUser() {
if (this.props.isSignedIn) {
return (
<h1>`The user ID is: ${this.props.userId}`</h1>
);
}
}
}
Sample.propTypes = {
isSignedIn: PropTypes.bool.isRequired,
userId: PropTypes.number.isRequired,
fetchUsers: PropTypes.func.isRequired,
};
const mapStateToProps = (state) => ({
isSignedIn: state.isSignedIn,
userId: state.userId,
});
const mapDispatchToProps = (dispatch) => ({
fetchUsers: () => {
dispatch(fetchUsers());
},
});
export default connect(mapStateToProps, mapDispatchToProps)(Sample);
You can also see here for my index.js
import React from 'react';
import {Provider} from 'react-redux';
import {ConnectedRouter} from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';
import App from './app';
const history = createHistory();
const store = configureStore(history);
render(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>, document.getElementById('app'),
);

Related

React Redux prop is undefined

Beginner question.
I want to pass a user object to a component from store as a prop, but the component doesn't get it (undefined). I get the user object from a third party service authentication service (google firebase)
The middleware actually logs out in the console that the action of type SET_CURRENT_USER takes place, and next state indeed will have a user.currentUser set to the object returned from the login service (NOT UNDEFINED).
However, the component doesn't re-render and doesn't seem to receive the object as prop
The component, in which the prop is undefined
import React from 'react';
import { connect } from 'react-redux';
import { auth } from "../../firebase/firebase.utils";
export const Navbar = ({ currentUser }) => {
return (
/* A LOT OF JSX CODE. currentUser IS UNDEFINED */
);
};
const mapStateToProps = state => ({
currentUser: state.user.currentUser
});
export default connect(mapStateToProps)(Navbar);
The App component, which has the above component as a child. Also, I'm trying to set the store to contain the user object in the componentDidMount()
import React from 'react';
import Homepage from "./pages/homepage";
import { Route, Switch } from 'react-router-dom';
import { connect } from 'react-redux';
import Login from "./pages/login";
import Register from "./pages/register";
import { Navbar } from "./components/navbar/navbar";
import { auth } from "./firebase/firebase.utils";
import { setCurrentUser } from "./redux/user/user.actions";
class App extends React.Component {
unsubscribeFromAuth = null;
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if(userAuth) {
(async () => {
const rawResponse = await fetch(/* JUST AN ASYNC FUNCTION TO POST TO BACKEND*/);
})();
}
this.props.setCurrentUser(userAuth); /*HERE IM TRYING TO SET THE STORE*/
})
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
return (
<div>
<Navbar /> /* THE COMPONENT WHICH SHOULD GET THE USER OBJECT AS PROP */
<Switch>
<Route exact={true} path={'/register'} component={Register} />
<Route exact={true} path={'/login'} component={Login} />
<Route path={'/'} component={Homepage} />
</Switch>
</div>
);
}
}
const mapDispatchToProps = (dispatch) => ({
setCurrentUser: user => dispatch(setCurrentUser(user))
});
export default connect(null, mapDispatchToProps)(App);
The index component
import React from 'react';
import ReactDOM from 'react-dom';
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux';
import store from "./redux/store";
ReactDOM.render(
<Provider store={store} > /* HERE IS STORE PROVIDED FROM IMPORT*/
<BrowserRouter>
<App/>
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
Root reducer
import { combineReducers } from "redux";
export default combineReducers({
user: userReducer
});
User reducer
const INITIAL_STATE = {
currentUser: null
};
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SET_CURRENT_USER':
return {
...state,
currentUser: action.payload
};
default:
return state;
}
};
export default userReducer;
User action
export const setCurrentUser = user => ({
type: 'SET_CURRENT_USER',
payload: user
});
The store
import { createStore, applyMiddleware } from "redux";
import logger from 'redux-logger';
import rootReducer from './root-reducer';
const middlewares = [logger];
const store = createStore(rootReducer, applyMiddleware(...middlewares));
export default store;
You're doing both a named and default export for Navbar. The default export gets wrapped by the connect HOC that adds currentUser to its props. The named export does not.
You import it named like this: import { Navbar } from. Instead use the default export: import Navbar from.
Then I would suggest removing the named export to avoid future confusion.

How can I maintain my store's state while using react-router and redux?

I am building an app prototype that essentially simulates ecommerce. I have components that each have different items that can be added to a cart(below I just show an example of how one would basically work). These components are accessed via different routes using the react-router. There is a header component that displays the number of items currently in the cart. The header gets the number of items in the cart from the state in the redux store. However, if I navigate to a new route, the store goes back to the default state. I need the the store to keep its state when a new route is navigated to. For example, if I go to the ShoppingPage, add an item to the cart, and then go back to the Home page, I need the cart to still have an item in it.
actions.js
export const actionTypes = Object.freeze({
UPDATE_CART: Symbol('UPDATE_CART'),
});
export const updateCart = (payload) => {
return {
type: actionTypes.UPDATE_CART,
payload,
};
};
export default actionTypes;
reducer.js
import actions from './actions';
export const INITIAL_STATE = {
cart: [],
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case actions.UPDATE_CART: {
return {
...state,
cart: action.payload,
};
}
default: {
return state;
}
};
};
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducer, { INITIAL_STATE } from './reducer';
const store = createStore(reducer, INITIAL_STATE);
console.log(store.getState());
ReactDOM.render(
<Provider store ={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
serviceWorker.unregister();
ShoppingPage.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { updateCart } from './actions';
class ShoppingPage extends Component {
addToCart = () => {
const cart = [...this.props.cart];
cart.push('new item');
this.props.modifyCart(cart);
render() {
return(
<div>
<button onClick={addToCart}>
Add To Cart
</button>
</div>
)
}
}
const mapDispatchToProps = dispatch => ({
modifyCart: payload => dispatch(updateCart(payload)),
});
const mapStateToProps = state => ({
cart: state.cart,
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(ShoppingPage);
Home.js
import React, { Component } from 'react';
import { ListGroup, ListGroupItem } from 'reactstrap';
class Home extends Component {
render() {
return(
<div>
<ListGroup>
<ListGroupItem><a href='/ShoppingPage'>ShoppingPage</a></ListGroupItem>
</div>
)
}
}
export default Home;
Header.js
import React, { Component } from 'react';
import { Navbar, NavbarBrand } from 'reactstrap';
import { connect } from 'react-redux';
class Header extends Component {
render() {
return(
<Navbar sticky='top' className='nav'>
<NavbarBrand href='/'>Online Shopping</NavbarBrand>
<span>{'Items in Cart: '}{this.props.cart.length}</span>
</Navbar>
)
}
}
const mapStateToProps = state => ({
cart: state.cart,
});
export default connect(
mapStateToProps
)(Header);
Routes.js
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './Home';
import ShoppingPage from './ShoppingPage';
const Routes = () => (
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/ShoppingPage' component={ShoppingPage} />
</Switch>
);
export default Routes;
App.js
import React from 'react';
import Routes from './Routes';
import Header from './Header';
function App() {
return (
<div>
<Header />
<Routes />
</div>
);
}
export default App;
What's likely happening is that during navigation the web app "reloads" again (which is wiping the redux state). In order to navigate with react router you want to look at <Link>.
For example,
Home.js
<a href='/ShoppingPage'>ShoppingPage</a>
should be changed to:
<Link to="/ShoppingPage">ShoppingPage</Link>

React-native-Redux: could not find stored in either the context or props of connect(componentName)

I'm getting below error even i had defined store for root component.
click to enlarge image
Not sure why im getting this error even after defining store.
index.js (rootpage)
import React, {Component} from 'react';
import {initStore} from './redux/store';
import {Provider} from 'react-redux';
import App from './App.container';
const store = initStore();
class BuddApp extends Component {
render () {
return (
<Provider store={store}>
<App />
</Provider>
);
}
}
export default BuddApp;
This is app.container.js which is inside app.
app/app.container.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import Router from './routes';
import Proptypes from 'prop-types';
import {addNavigationHelpers} from 'react-navigation';
class App extends Component {
render () {
const {dispatch, nav, userPreferences} = this.props;
console.log(this.props)
return (
<Router screenProps={userPreferences} navigation={addNavigationHelpers({dispatch, state: nav})}/>
</Provider>
);
}
}
App.propTypes = {
dispatch: Proptypes.func,
nav: Proptypes.object,
userPreferences: Proptypes.object
};
const mapStateToProps = ({nav, userPreferences}) => ({
nav,
userPreferences
});
const mapDispatchToProps = (dispatch) => ({
dispatch
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
app/pages/login.page.js
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import Login from '../components/Login/Login.component';
import {NavigationActions} from 'react-navigation';
class LoginPage extends Component {
onLoginPress(){
console.log("fsdfds")
this.props.navigation.navigate('setupProfile')
}
render () {
const {state} = this.props;
return (
<Login onLoginPress={this.onLoginPress.bind(this)} />
);
}
}
LoginPage.propTypes = {
onLoginPress: PropTypes.func
};
const mapStateToProps = (state) => ({
state:state
});
const mapDispatchToProps = (dispatch) => ({
saveLoginDetails: dispatch(addProfile(f))
});
export default connect(mapStateToProps,mapDispatchToProps)(LoginPage);
This results in getting the above mentioned error. as you can see I am passing the store in the same manner as shown in the redux example.
Am i defined store in wrong file?
Try this way:
import { createStore, applyMiddleware } from "redux"
const store = createStore(
<<Your combined reducers comes here>>,
applyMiddleware(<<All your middleware comes here>>))

reducer switch statements not working

I've just started out with Redux and trying to implement a simple MERN App (for practice).
Everything in my code is working fine, but my reducer function is showing unexpected behaviour. When an action (which gets fetches data from express api) is called my reducer correctly goes to the particular switch case data logs successfully but then three times the default case is passed and data on my component which I log is showing null. Please Help.
Here's my code:-
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import articlesReducer from './store/reducers/articlesReducer';
import registerServiceWorker from './registerServiceWorker';
const rootReducer = combineReducers({
articles: articlesReducer
});
const store = createStore(rootReducer, applyMiddleware(thunk));
const app = (
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>
);
ReactDOM.render(app, document.getElementById('root'));
registerServiceWorker();
App.js
import React, {Component} from 'react';
import {Switch, Route} from 'react-router-dom';
import './App.css';
import Home from './components/Home/Home';
class App extends Component {
render() {
return (
<div>
<Switch>
<Route exact path="/" component={Home} />
</Switch>
</div>
);
}
}
export default App;
articles.js
export const getAllArticles = () => {
return dispatch => {
return (
fetch('http://localhost:5000/api/articles')
.then(res => res.json())
.then(data => {
dispatch({type: 'GET_ALL_ARTICLES', articles: data})
})
);
};
};
articlesReducer.js
const initialState = {
articles:null
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'GET_ALL_ARTICLES':
console.log('in reducer', action.type, action.articles[0]);
return {
...state,
articles: action.articles
};
default:
console.log('In default');
return state;
}
};
export default reducer;
myComponent
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getAllArticles } from '../../store/actions/articles.js';
class MainPage extends Component {
componentWillMount() {
this.props.initArticles();
console.log(this.props.articles);
}
render() {
return (
<div className="container">
<br />
<h1>Here comes the articles!!</h1>
</div>
);
}
}
const mapStateToProps = state => {
return {
articles: state.articles
};
};
const mapDispatchToProps = dispatch => {
return {
initArticles: () => dispatch(getAllArticles())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MainPage);
The output in my console is somewhat like this:-
In default
In default
In default
{articles: null}
in reducer GET_ALL_ARTICLES {articles[0] Object}
I don't know what is the mistake. Thanks for help in advance.
I'm not sure whether this is actually the problem but you incorrectly access the articles. You have a root reducer with articles reducer:
const rootReducer = combineReducers({
articles: articlesReducer
});
which initial state is:
const initialState = {
articles:null
};
And in your mapDispatchToProps you "import" whole reducer state:
const mapStateToProps = state => {
return {
articles: state.articles
};
};
I think you wanted to access articles property
const mapStateToProps = state => {
return {
articles: state.articles.articles
};
};
Other than that everything seems to be fine. I would however as pointed in comment initialize articles as empty array [].
const initialState = {
articles: []
};

Could not find "store" in either the context or props of "Connect(App)"

I'm having problems with misconfigured Redux after merging contents of multiple files into one to serve as config for Redux.
How to resolve store, while keeping this in one file?
Unhandled JS Exception: Could not find "store" in either the context
or props of "Connect(App)". Either wrap the root component in a
, or explicitly pass "store" as a prop to "Connect(App)".
'use strict';
import React, { Component } from 'react';
import { createStore, applyMiddleware, combineReducers, bindActionCreators } from 'redux';
import { Provider, connect } from 'react-redux';
import thunk from 'redux-thunk';
import * as screenActions from './redux/actions/screenActions';
import * as reducers from './redux/stores/reducers';
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer);
import RootContainer from './redux/views/containers/rootContainer';
class App extends Component {
render() {
const { state, actions } = this.props;
return (
<Provider store={store}>
<RootContainer />
</Provider>
);
}
}
export default connect(
(state) => ({
state: state.reducer
}),
(dispatch) => ({
actions: bindActionCreators(screenActions, dispatch)
})
)(App);
Provider, passes the store to the component nested within it and generally only needed to be applied to the root component (in your case <RootContainer>
connect connect with the component providing store and not the component that has store provided to it.
SUGGESTED SOLUTION:
MOVE the connect to the <RootContainer> component file instead, and pass connect the RootContainer and not the App component:
export default connect(
(state) => ({
state: state.reducer
}),
(dispatch) => ({
actions: bindActionCreators(screenActions, dispatch)
})
)(RootContainer); // <<--- here :)
UPDATE:
Given the OP wants to achieve all of this in the same file, you'll have to create a React element that represents your Redux container created from connect, so:
'use strict';
import React, { Component } from 'react';
import { createStore, applyMiddleware, combineReducers, bindActionCreators } from 'redux';
import { Provider, connect } from 'react-redux';
import thunk from 'redux-thunk';
import * as screenActions from './redux/actions/screenActions';
import * as reducers from './redux/stores/reducers';
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer);
import RootContainer from './redux/views/containers/rootContainer';
// name has to be capitalised for React to recognise it as an element in JSX
const ConnectedRoot = connect(
(state) => ({
state: state.reducer
}),
(dispatch) => ({
actions: bindActionCreators(screenActions, dispatch)
})
)(RootContainer);
class App extends Component {
render() {
const { state, actions } = this.props;
return (
<Provider store={store}>
<ConnectedRoot /> <------USE IT HERE
</Provider>
);
}
}
I had such a problem when I used:
import connect from "react-redux/lib/connect/connect";
instead of that, use:
import {connect} from "react-redux";
In your root index put the provider.
<Provider store={store}>
<App />
</Provider>,

Categories

Resources