How to use Redux state in _app.tsx (NextJS) - javascript

This is the code to my _app.tsx
import '../styles/globals.css'
import AppNav from '../components/AppNav'
import type { AppProps } from 'next/app'
import { store } from '../store'
import { Provider } from 'react-redux'
import { useSelector, useDispatch } from 'react-redux'
import { RootState } from '../store'
function MyApp({ Component, pageProps }: AppProps) {
const user = useSelector((state: RootState) => state.user.value)
return (
<Provider store={store}>
<div className='global_container'>
<AppNav />
<Component {...pageProps} />
</div>
</Provider>
)
}
export default MyApp
In the code, I am trying to use the user redux state inside _app.tsx, but it gives me an error: Error: could not find react-redux context value; please ensure the component is wrapped in a Provider.
How can I access the redux state inside _app.tsx? This is nextjs by the way.

Your component is not connected to the store. Normally this package https://github.com/kirill-konshin/next-redux-wrapper is used to wrap next.js. Once you created wrapper, you have to wrap _app.jx
export default wrapper.withRedux(MyApp);
Now you can use useSelector to access store within any component
import { useSelector } from "react-redux";

Related

React-alert library does not seem to be re-rendering when state changes

So I'm trying to do error handling using a library called react-alert.
I'm using Axios as my REST handler. I created an action and a reducer, which are both working fine; saw my state update in my DevTools extension when I make a post request.
Here's my functional component called Alert.js
import React, { Fragment, useState } from "react";
import { useAlert } from "react-alert";
import { useSelector } from "react-redux";
import { useEffect } from "react";
export default function Alert(props) {
const alert = useAlert();
const error = useSelector((state) => state.errors.errors);
const [errorMSG, setERRORS] = useState();
useEffect(() => {
if (error !== errorMSG) {
setERRORS(error);
alert.show(errorMSG);
} else {
alert.show("Welcome!");
}
}, [errorMSG]);
return <Fragment />;
then I called it in my main App.js
//Main Imports
import React, { Component, Fragment } from "react";
import ReactDOM from "react-dom";
// React Alert
import { Provider as AlertProvider } from "react-alert";
import AlertTemplate from "react-alert-template-basic";
import Alert from "./layout/Alert";
const alertOptions = {
timeout: 3000,
position: "top center",
};
//Components import
import Header from "./layout/Header";
import Dashboard from "./products/Dashboard";
//Redux import
import { Provider } from "react-redux";
import store from "../store";
class App extends Component {
render() {
return (
<Provider store={store}>
<AlertProvider template={AlertTemplate} {...alertOptions}>
<Alert />
<Fragment>
<Header />
<div className="container-fluid">
<Dashboard />
</div>
</Fragment>
</AlertProvider>
</Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
I do see the Welcome! when the app mounts, but when I have an error I see nothing. Could anyone help me find a solution to this; would be really appreciated.
You see "Welcome" because when your component mounts first time, it calls the useEffect hook. But the useEffect hook isn't getting triggered afterwards because of the wrong dependency item provided for the useEffect. You need to add error instead of errorMSG, because error is coming from your store and you want to show the message when there is a new error emitted:
useEffect(() => {
if (error !== errorMSG) {
setERRORS(error);
alert.show(errorMSG);
} else {
alert.show("Welcome!");
}
}, [error]);

Import syntax React Redux

Hello I am working on Shopping List and my file looks like this:
import React, { Component } from 'react';
import AppNavbar from './components/AppNavbar';
import ShoppingList from './components/ShoppingList';
import {Provider} from 'react-redux';
import store from './';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
function App() {
return (
<Provider store={store}>
<div className="App">
<AppNavbar/>
<ShoppingList/>
</div>
</Provider>
);
}
export default App;
However, Ireceived this error:
Failed to compile
./src/App.js
Attempted import error: './' does not contain a default export (imported as 'store').
can someone please help fix this ?
The problem in your code is how you are trying load the store with import store from './';.
Instead you need to use createStore() from redux as the following:
import { createStore, applyMiddleware } from 'redux';
const store = createStore(
/* your reducers */,
applyMiddleware( /* your middleware */ ),
);
What you can pass to the <Provider /> as follows:
ReactDOM.render(<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
Read further here about createStore(reducer, \[preloadedState\], \[enhancer\]) which:
Creates a Redux store that holds the complete state tree of your app. There should only be a single store in your app.
I hope this helps!
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers';
import Posts from './containers/Posts'
import NavBar from './components/NavBar';
// Apply Thunk middleware
const middleware = applyMiddleware(thunk);
// Create store
const store = createStore(reducers, enhancer);
class App extends Component {
render() {
return (
<Provider store={store}>
<React.Fragment>
<NavBar />
<Posts />
</React.Fragment>
</Provider>
);
As you can see, the app.js page should be looked like this. You can't just import store from another file. It's better to use best practices like this.
Now you will wonder how the reducers came from. Generally, developers put all the reducers in a reducers folder. In that folder you can have a index.js file and other reducer files. For now, you better use a empty main reducer index.js like below.
import { combineReducers } from 'redux'
const rootReducer = combineReducers({
null
})
export default rootReducer;
If you something like post or whatever, the code should be modified like this.
import { combineReducers } from 'redux'
import posts from './posts';
const rootReducer = combineReducers({
posts: posts
})
export default rootReducer;
Simply, posts is a post reducer in the reducers folder. Selecting a appropriate middleware is up to you. I have used redux-thunk. You can use redux-saga, redux-logic, or custom middleware. I think this will help you to solve your problem.

Next.js - How do I use Provider to Wrap Routes and use Context with Hooks

I wrote code similar to the following in create-react-app and I want to know the equivalent for next.js. The code below is my attempt at having a global Context that is available to all pages. The Provider wraps the Links. I get no errors. The problem is inside the about page the console.log(state) returns undefined when I expect the Context state. How do I fix this?
Thank you.
pages/index.js
import Link from "next/link";
import {Provider} from './Context';
function Index(){
return(
<div>
<Provider>
<ul>
<li><Link href="/"><a>Home</a></Link></li>
<li><Link href="/about"><a>About</a></Link></li>
</ul>
</Provider>
</div>
)
}
export default Index;
pages/about.js
import { useRouter } from 'next/router';
import {Context} from './Context';
import {useContext} from 'react';
const About= () => {
const data = useContext(Context);
console.log(data)
return (
<div>
<p>This is the blog post content.</p>
</div>
);
};
export default About;
pages/Context.js
import React, {createContext, useState, useEffect}from 'react';
let Context = createContext();
function Provider(props){
const initialState = {
userID: false,
user:undefined,
loading: true,
authenticated:false
}
const [state,updateState] = useState(initialState)
return(
<Context.Provider value={{
state:state
}}>
{props.children}
</Context.Provider>
)
}
const Consumer = Context.Consumer;
export {Provider, Consumer, Context}
You can move <Provider> into a custom <App> component which initializes each page.
pages/_app.js
import React from 'react'
import App from 'next/app'
import {Provider} from './Context';
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return <Provider><Component {...pageProps} /></Provider>
}
}
export default MyApp
More info here
The idea is you need to have a parent Provider anywhere in your tree to consume a context. In your case, you Provider is not a parent of About component. You need to move your Provider to _app.js like this

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>,

Could not find store in either context or props

I am writing code with react and I just started using redux (because I require a container of sorts). However, I have been stuck at one place for a bit now.
I get this error -
Invariant Violation: Could not find "store" in either the context or
props of "Connect(HomePage)". Either wrap the root component in a
, or explicitly pass "store" as a prop to
"Connect(HomePage)".
I tried googling, and according to the troubleshooting section of react-redux, this can be checked using these three things:
1. Make sure you don’t have a duplicate instance of React on the page.
2. Make sure you didn’t forget to wrap your root component in < Provider>.
3. Make sure you’re running the latest versions of React and React Redux.
I have the following code that is the root (which is where the store is defined with the provider) -
import React from 'react';
import { Router, browserHistory } from 'react-router';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reduxThunk from 'redux-thunk';
import routes from '../Routes';
import reducers from './reducers/reducers';
import actions from './actions/actions';
export default class AppRoutes extends React.Component {
render() {
const store = createStore(reducers, applyMiddleware(reduxThunk));
return (
<Provider store={store}>
<Router history={browserHistory} routes={routes} onUpdate={() => window.scrollTo(0, 0)}/>
</Provider>
);
}
}
And this error only happens on one of the two components I have -
// No error when connected only in this component
import React from 'react';
import { connect } from 'react-redux';
import * as actions from './actions/actions';
class Dashboard extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h1>Hello, {this.props.isAuthenticated.toString()}</h1>;
}
}
function mapStateToProps(state) {
return {
content: state.auth.content,
isAuthenticated: state.auth.authenticated
};
}
export default connect(mapStateToProps, actions)(Dashboard);
// Error thrown when I try to connect this component
import React from 'react';
import LoginPage from './LoginPage';
import Dashboard from './Dashboard';
import Loading from './Loading';
import { connect } from 'react-redux';
import * as actions from './actions/actions';
class HomePage extends React.Component {
constructor(props) {
super(props);
this.setState({
loading: true
});
}
render() {
var inPage = undefined;
if(this.props.isAuthenticated) {
console.log('Logged in');
inPage = <Dashboard user = {HomePage.user}/>;
}
else if (this.state.loading){
console.log('Loading');
inPage = <Loading />;
}
else {
console.log('Login');
inPage = <LoginPage />;
}
return (
<div>
{inPage}
</div>
);
}
}
function mapStateToProps(state) {
this.setState({
loading: false
});
return {
content: state.auth.content,
isAuthenticated: state.auth.authenticated
}
}
export default connect(mapStateToProps, actions)(HomePage);
Not sure if this is where your issue lies or not. So I may be way off base.
But, I would check to be sure you are passing down the children to your components. The way I've done this is in my App class as such:
import React, {PropTypes} from 'react';
import { connect } from 'react-redux';
class App extends React.Component{
render(){
return(
<div className="container-fluid">
{this.props.children}
</div>
);
}
}
App.propTypes={
children: PropTypes.object.isRequired
};
export default connect()(App);

Categories

Resources