I am following the tutorial on linked in for react with Redux and Next.js. the code is working fine until I added Redux. I cannot understand the error below, it does not give me much info. Can some one explain to me where is the issue.
the tutorial did come with Exercise files but even they have the same issue. It could be something in my setup perhaps.
No page context
Error: No page context
at Function._callee$ (C:\learnings\SSR\node_modules\next-redux-wrapper\lib\index.js:164:23)
at tryCatch (C:\learnings\SSR\node_modules\regenerator-runtime\runtime.js:62:40)
at Generator.invoke [as _invoke] (C:\learnings\SSR\node_modules\regenerator-runtime\runtime.js:288:22)
at Generator.prototype.(anonymous function) [as next] (C:\learnings\SSR\node_modules\regenerator-runtime\runtime.js:114:21)
at asyncGeneratorStep (C:\learnings\SSR\node_modules\next-redux-wrapper\lib\index.js:18:103)
at _next (C:\learnings\SSR\node_modules\next-redux-wrapper\lib\index.js:20:194)
at C:\learnings\SSR\node_modules\next-redux-wrapper\lib\index.js:20:364
at new Promise (<anonymous>)
at Function.<anonymous> (C:\learnings\SSR\node_modules\next-redux-wrapper\lib\index.js:20:97)
at Function.getInitialProps (C:\learnings\SSR\node_modules\next-redux-wrapper\lib\index.js:205:22)
at _callee$ (C:\learnings\SSR\node_modules\next\dist\lib\utils.js:86:30)
at tryCatch (C:\learnings\SSR\node_modules\regenerator-runtime\runtime.js:62:40)
at Generator.invoke [as _invoke] (C:\learnings\SSR\node_modules\regenerator-runtime\runtime.js:288:22)
at Generator.prototype.(anonymous function) [as next] (C:\learnings\SSR\node_modules\regenerator-runtime\runtime.js:114:21)
at asyncGeneratorStep (C:\learnings\SSR\node_modules\#babel\runtime-corejs2\helpers\asyncToGenerator.js:5:24)
at _next (C:\learnings\SSR\node_modules\#babel\runtime-corejs2\helpers\asyncToGenerator.js:27:9)
My Index.js looks like this
import React from 'react';
import { bindActionCreators } from 'redux';
import { initStore, initialPosters, addItem } from '../store';
import withRedux from 'next-redux-wrapper';
import './index.css';
import Poster from './Poster';
class Index extends React.Component {
static async getInitialProps ({ store }) {
store.dispatch(initialPosters());
}
render () {
return (
<div className="App">
<header className="App-header">
<img src="/static/logo.png"
className="static-logo" alt="logo"
/>
</header>
<div className="Grid">
{
this.props.Posters.map((Poster) => (
<Poster key={Poster.id} />
))
}
</div>
</div>
)
}
};
const mapDispatchToProps = (dispatch) => {
return {
initialPosters: bindActionCreators(initialPosters, dispatch),
addItem: bindActionCreators(addItem, dispatch)
}
}
const mapStateToProps = (state) => {
return {
Posters: state.Posters,
}
}
export default withRedux(initStore, mapStateToProps, mapDispatchToProps)(Index);
my store.js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunkMiddleware from 'redux-thunk';
import data from './data/data.json';
// initial state
const startState = {
posters: []
}
// Actions
export const initialposters = () => {
return {
type: 'INITIALPOSTERS',
posters: data
}
}
export const addItem = (item) => {
return {
type: 'ADD',
item
}
}
// reducers
export const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INITIALPOSTERS':
return {
posters: action.posters,
}
case 'ADD':
return {
...state,
posters: [...state.posters, action.item]
}
default: return state
}
}
// create store
export const initStore = (initialState = startState) => {
return createStore(reducer, initialState, composeWithDevTools(applyMiddleware(thunkMiddleware)));
}
the Poster.js
import './Poster.css';
const Poster = (props) => (
<div className="Poster">
<div className="front">
<img src="/static/product.jpg" alt="Avatar" className="Poster-image" />
<div className="container">
<h3>A product <span className="price">$24.99</span></h3>
<p>A description </p>
</div>
</div>
</div>
);
export default Poster;
The error stack trace suggests line 164 in index.js in next-redux-wrapper which ends up here
case 2:
if (appCtx.ctx) {
_context.next = 4;
break;
}
throw new Error('No page context');
I am very new to react and redux so i am not able to make anything out of it. the good thing is its not working so there is opportunity to solve something here :D
it is a paid tutorial ; so not sure if this link would work for people.
https://www.lynda.com/React-js-tutorials/React-Server-Side-Rendering/679641-2.html
Related
Okay so I am following a tutorial, and I'm a beginner. This is my first experience with Redux.
This is the error I've been getting when it should be displaying the home screen of my webpage.
Actions must be plain objects. Instead, the actual type was: 'string'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.
I have been searching everywhere but it looks to me like I applied thunk correctly. I'm hoping someone more experienced will be able to spot my mistake. Thank you.
HomeScreen.js
import React, { useEffect } from 'react';
import {Link} from 'react-router-dom';
import { listProducts } from '../actions/productActions.js';
import { useDispatch, useSelector } from 'react-redux';
function HomeScreen() {
const productList = useSelector(state => state.productList);
const { products, loading, error} = productList;
const dispatch = useDispatch();
useEffect(() => {
dispatch(listProducts());
return () => {
//
};
}, [])
return loading? <div>Loading...</div> :
error? <div>{error}</div>:
<ul className="products">
{
products.map(product =>
<li key={product._id}>
<div className="product">
<Link to={'/product/' + product._id}>
<img className="product-image" src={product.image} alt="product" />
</Link>
<div className="product-name">
<Link to={'/product/' + product._id}>{product.name}</Link>
</div>
<div className="product-brand">{product.brand}</div>
<div className="product-price">${product.price}</div>
<div className="product-rating">{product.rating} Stars ({product.numReviews} Reviews)</div>
</div>
</li>)
}
</ul>
}
export default HomeScreen;
store.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { productListReducer } from './reducers/productReducers.js';
import thunk from 'redux-thunk';
import * as compose from 'lodash.flowright';
const initialState = {};
const reducer = combineReducers({
productList: productListReducer,
})
const composeEnhancer = window.__REDUXDEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, initialState, composeEnhancer(applyMiddleware(thunk)));
export default store;
productActions.js
import { PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS } from "../constants/productconstants.js";
import axios from "axios";
const listProducts = () => async (dispatch) => {
try {
dispatch(PRODUCT_LIST_REQUEST);
const {data} = await axios.get("/api/products");
dispatch({type: PRODUCT_LIST_SUCCESS, payload: data});
}
catch (error) {
dispatch({type: PRODUCT_LIST_FAIL, payload:error.message});
}
}
export {listProducts};
productReducers.js
import { PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS } from "../constants/productconstants";
function productListReducer(state= {products: [] }, action) {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return {loading:true};
case PRODUCT_LIST_SUCCESS:
return {loading:false, products: action.payload};
case PRODUCT_LIST_FAIL:
return {loading:false, error: action.payload};
default:
return state;
}
}
export { productListReducer }
PRODUCT_LIST_REQUEST appears to be a string. You cannot dispatch a string by itself - only action objects. Actions are always objects that have a type field inside, like {type: 'counter/incremented'}.
That said, you should be using our official Redux Toolkit package to write your Redux code. Redux Toolkit will simplify all of the Redux store setup and reducer logic you've shown.
I'm trying to setup an AppProvider component that uses a context and reducer to handle global data for my app. However it looks like the useReducer hook that is called within AppProvider is causing an error. I've read the link in the error message and as far as I can tell I'm not violating any of the rules listed there.
import { merge } from 'lodash';
import React, { useReducer, Reducer, useContext, Dispatch } from 'react';
export interface AppState {
stuff: string;
}
const initialState: AppState = { stuff: 'something' };
const AppStateContext = React.createContext(initialState);
export type Action = { type: 'SET_STATE'; state: Partial<AppState> };
const AppDispatchContext = React.createContext<Dispatch<Action>>(
() => initialState,
);
const reducer: Reducer<AppState, Action> = (
state: AppState,
action: Action,
): AppState => {
switch (action.type) {
case 'SET_STATE':
return { ...merge(state, action.state) };
default:
return { ...state };
}
};
// eslint-disable-next-line #typescript-eslint/no-explicit-any
export type AppProviderProps = AppState & { children: any };
export const AppProvider = ({ children, ...rest }: AppProviderProps) => {
const [state, dispatch] = useReducer(reducer, {
...initialState,
...rest,
});
return (
<AppDispatchContext.Provider value={dispatch}>
<AppStateContext.Provider value={state}>
{children}
</AppStateContext.Provider>
</AppDispatchContext.Provider>
);
};
// Hooks
export const useAppContextState = (): AppState => useContext(AppStateContext);
export const useAppContextDispatch = (): React.Dispatch<Action> =>
useContext(AppDispatchContext);
I'm using it with a new app created with create-react-app:
import queryString from 'query-string';
import React from 'react';
import { AppProvider } from './app/AppProvider';
import logo from './logo.svg';
import './App.css';
function App() {
const { stuff } = queryString.parse(document.location.search);
if (typeof stuff !== 'string') {
return (
<div>
Missing stuff parameter.
</div>
);
}
return (
<AppProvider stuff={stuff}>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
</AppProvider>
);
}
export default App;
The error thrown:
Package versions:
react#17.0.2 (the only version installed)
react-dom#17.0.2
react-scripts#4.0.3
The issue was in fact caused by having two different react packages installed. The app was part of a monorepo and yarn hoisted the react-dom package. The fix was to simply prevent hoisting react and react-dom:
In package.json:
{
"workspaces": {
"packages": [
"packages/*"
],
"nohoist": [
"**/react",
"**/react/**",
"**/react-dom",
"**/react-dom/**",
]
}
}
i've been working on a project trying to learn redux with react. But there is an error and i don't know exactly how to fix it. Files/codes in down if you need more information about how store works.
store/index.js
import { createStore, applyMiddleware, compose } from "redux";
import rootReducer from "./reducers";
import thunk from 'redux-thunk'
const middlewares = [thunk]
const store = createStore(rootReducer, compose(applyMiddleware(...middlewares), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()))
export default store;
actions/index.js
import axios from "axios"
export const increment = (payload) => {
return {
type: 'INCREMENT',
payload: payload
}
}
export const decrement = () => {
return {
type: 'DECREMENT'
}
}
export const fetch = () => {
return async (dispatch) => {
axios.get('https://jsonplaceholder.typicode.com/posts/')
.then(data => dispatch({type: 'FETCH', payload: data.data}))
}
}
store/todos.js
const todos = (state = [], action) => {
switch(action.type){
case 'FETCH':
return Object.assign(state, action.payload);
default:
return state;
}
}
export default todos;
App.js
import logo from './logo.svg';
import './App.css';
import {useSelector, useDispatch, connect} from 'react-redux'
import {increment, decrement, fetch} from './store/actions/'
import { GeistProvider, CssBaseline } from '#geist-ui/react';
function App(props) {
const count = useSelector((state) => state.counter)
const todos = useSelector((state) => state.todos)
const dispatch = useDispatch()
return (
<div className="App">
<header className="App-header">
<h1>Count is {count}</h1>
<button onClick={() => dispatch(increment(3))}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(fetch())}>FETCH</button>
{todos.length ? todos[0].title : <h1>Not fetched.</h1>}
</header>
</div>
);
}
export default App;
This is the codes in project. Let me know if you guys need more information about anything. Thanks for help!
You are wrong at return Object.assign(state, action.payload);. It's mutated state so redux can't detect state change. You should read this https://redux.js.org/understanding/thinking-in-redux/three-principles#changes-are-made-with-pure-functions
You can change to this
return Object.assign({}, state, action.payload);
or this
return { ...state, ...action.payload }
I'm having trouble connecting my container components to the redux store, I'm not sure exactly where the connection is supposed to happen, whether in the container component or the component that will be dispatching an action. currently, my index.js looks like this
import React from "react";
import reactDOM from "react-dom";
import App from "./app.jsx";
import storeFactory from "./store";
import { Provider } from 'react-redux';
const store = storeFactory();
reactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("app")
);
currently, my store factory function looks like this
import rootReducer from "../reducers";
import { createStore, applyMiddleware } from "redux";
import { fetchProductInformation } from "../actions";
const storeData = {
productInformation: {}
};
const storeFactory = (initialState = storeData) => {
applyMiddleware(fetchProductInformation)(createStore)(
rootReducer,
localStorage["redux-store"]
? JSON.parse(localStorage["redux-store"])
: storeData
);
};
export default storeFactory;
my container component is
import SearchBar from '../components/searchbar.jsx';
import Nutrients from "../components/Nutrients.jsx";
import { fetchProductInformation } from '../actions';
import { connect } from 'react-redux';
const newSearch = (props) => (
<SearchBar
className="searchbar searchbar_welcome"
onNewProduct={( name ) => (props.fetchProductInformation(name))}
/>
)
const productInformation = (props) => {
const { nutrients, name } = props;
return nutrients.length > 1 ?
(
<div>
<newSearch />
<h3>{name}</h3>
<hr/>
<Nutrients
className="nutrientInformation"
list={nutrients}
/>
</div>
)
: null
}
const mapStateToProps = ({nutrients, name}) => ({
nutrients,
name
});
const mapDispatchToProps = dispatch => ({
fetchProductInformation: name => {
dispatch(fetchProductInformation(name))
}
});
export const Search = connect(null, mapDispatchToProps)(newSearch);
export const productInfo = connect(mapStateToProps)(productInformation);
when i run the code i get the following error
Provider.js:19 Uncaught TypeError: Cannot read property 'getState' of undefined
at Provider.js:19
at mountMemo (react-dom.development.js:15669)
at Object.useMemo (react-dom.development.js:15891)
at useMemo (react.development.js:1592)
at Provider (Provider.js:18)
at renderWithHooks (react-dom.development.js:15108)
at mountIndeterminateComponent (react-dom.development.js:17342)
at beginWork$1 (react-dom.development.js:18486)
at HTMLUnknownElement.callCallback (react-dom.development.js:347)
at Object.invokeGuardedCallbackDev (react-dom.development.js:397)
react-dom.development.js:19814 The above error occurred in the <Provider> component:
in Provider
Consider adding an error boundary to your tree to customize error handling behavior.
Visit react-error-boundaries to learn more about error boundaries.
Provider.js:19 Uncaught TypeError: Cannot read property 'getState' of undefined
at Provider.js:19
at mountMemo (react-dom.development.js:15669)
at Object.useMemo (react-dom.development.js:15891)
at useMemo (react.development.js:1592)
at Provider (Provider.js:18)
at renderWithHooks (react-dom.development.js:15108)
at mountIndeterminateComponent (react-dom.development.js:17342)
at beginWork$1 (react-dom.development.js:18486)
at HTMLUnknownElement.callCallback (react-dom.development.js:347)
at Object.invokeGuardedCallbackDev (react-dom.development.js:397)
from the errors shown i dont know exactly what the error is as it seems to be comming from the provider.js..
your code has some bug. here are fixed code.
import SearchBar from '../components/searchbar.jsx';
import Nutrients from "../components/Nutrients.jsx";
import { fetchProductInformation } from '../actions';
import { connect } from 'react-redux';
const newSearch = (props) => (
<SearchBar
className="searchbar searchbar_welcome"
onNewProduct={( name ) => (props.fetchProductInformation(name))}
/>
)
const productInformation = (props) => {
const { nutrients, name } = props;
return nutrients.length > 1 ?
(
<div>
<newSearch />
<h3>{name}</h3>
<hr/>
<Nutrients
className="nutrientInformation"
list={nutrients}
/>
</div>
)
: null
}
const mapStateToProps = ({nutrients, name}) => ({
nutrients,
name
});
const mapDispatchToProps = dispatch => ({
fetchProductInformation: name => {
dispatch(fetchProductInformation(name))
}
});
export const Search = connect(null, mapDispatchToProps)(newSearch);
export const productInfo = connect(mapStateToProps)(productInformation)
store.js
import rootReducer from "../reducers";
import { createStore } from "redux";
const storeData = {
productInformation: {}
};
const initialStore = localStorage["redux-store"] ? JSON.parse(localStorage["redux-store"]) : storeData;
const store = createStore(rootReducer, initialStore);
export default store;
index.js
import store from "./store";
...
<Provider store={store}>
...
I am trying to test nested connected components (Redux connected):
The Main component has Container component and are provided by store wrapper in App.
import React, { Component } from 'react';
import Main from './components/Main';
import './App.css';
import { Provider } from 'react-redux'
import createStore from './redux'
import applyConfigSettings from './config'
// Apply config overrides
applyConfigSettings()
const store = createStore()
class App extends Component {
render() {
return (
<Provider store={store}>
<Main/>
</Provider>
);
}
}
export default App;
Main.js
import React, { Component } from 'react';
import logo from '../logo.svg';
import { connect } from 'react-redux'
import Container from './Container'
export class Main extends Component {
render(){
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to Pomodoro App</h1>
</header>
<Container/>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
fetching: state.user.fetching,
}
}
const mapDispatchToProps = (dispatch) => {
return {
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Main)
Container.js
import React, { Component } from 'react';
import Grid from 'material-ui/Grid';
import { connect } from 'react-redux'
export class Container extends Component{
render(){
return (
<div className="grid-root">
</div>
)
}
}
const mapStateToProps = (state) => {
return {
fetching: state.user.fetching,
}
}
const mapDispatchToProps = (dispatch) => {
return {
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Container)
All this is created using Create-react-app library. I have installed Enzyme too for testing. Here is my test file for Main.test.js
import React from 'react';
import { shallow, mount } from 'enzyme';
import {Main} from '../../components/Main'
import ContainerConnect, {Container} from '../../components/Container';
import configureStore from 'redux-mock-store';
import { Provider, connect} from 'react-redux';
describe('Main', () => {
let wrapper;
let mountWrapper;
it('wraps all the contents in a div with .App class', () => {
wrapper = shallow(<Main />);
expect(wrapper.find('.App').length).toEqual(1);
});
it('wraps content of header in a div with .App-header class', () => {
wrapper = shallow(<Main />);
expect(wrapper.find('.App-header').length).toEqual(1);
});
it('mount', () => {
const middlewares = [];
const mockStore = configureStore(middlewares);
const initialState = {}
const store = mockStore(initialState)
mountWrapper = mount(
<Provider store={store}>
<Main />
</Provider>
);
});
})
I get following error for the last test:
FAIL src/tests/components/Main.test.js
● Console
console.error node_modules/react-dom/cjs/react-dom.development.js:9643
The above error occurred in the <Connect(Container)> component:
in Connect(Container) (at Main.js:16)
in div (at Main.js:11)
in Main (at Main.test.js:31)
in Provider (created by WrapperComponent)
in WrapperComponent
Consider adding an error boundary to your tree to customize error handling behavior.
Visit some link to fb to learn more about error boundaries.
● Main › mount
TypeError: Cannot read property 'fetching' of undefined
at Function.mapStateToProps [as mapToProps] (src/components/Container.js:18:30)
at mapToPropsProxy (node_modules/react-redux/lib/connect/wrapMapToProps.js:54:92)
at Function.detectFactoryAndVerify (node_modules/react-redux/lib/connect/wrapMapToProps.js:63:19)
at mapToPropsProxy (node_modules/react-redux/lib/connect/wrapMapToProps.js:54:46)
at handleFirstCall (node_modules/react-redux/lib/connect/selectorFactory.js:37:18)
at pureFinalPropsSelector (node_modules/react-redux/lib/connect/selectorFactory.js:85:81)
at Object.runComponentSelector [as run] (node_modules/react-redux/lib/components/connectAdvanced.js:43:25)
at Connect.initSelector (node_modules/react-redux/lib/components/connectAdvanced.js:195:23)
at new Connect (node_modules/react-redux/lib/components/connectAdvanced.js:136:15)
at constructClassInstance (node_modules/react-dom/cjs/react-dom.development.js:6801:20)
at updateClassComponent (node_modules/react-dom/cjs/react-dom.development.js:8336:9)
at beginWork (node_modules/react-dom/cjs/react-dom.development.js:8982:16)
at performUnitOfWork (node_modules/react-dom/cjs/react-dom.development.js:11814:16)
at workLoop (node_modules/react-dom/cjs/react-dom.development.js:11843:26)
at renderRoot (node_modules/react-dom/cjs/react-dom.development.js:11874:9)
at performWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:12449:24)
at performWork (node_modules/react-dom/cjs/react-dom.development.js:12370:9)
at performSyncWork (node_modules/react-dom/cjs/react-dom.development.js:12347:5)
at requestWork (node_modules/react-dom/cjs/react-dom.development.js:12247:7)
at scheduleWorkImpl (node_modules/react-dom/cjs/react-dom.development.js:12122:13)
at scheduleWork (node_modules/react-dom/cjs/react-dom.development.js:12082:12)
at scheduleRootUpdate (node_modules/react-dom/cjs/react-dom.development.js:12710:5)
at updateContainerAtExpirationTime (node_modules/react-dom/cjs/react-dom.development.js:12738:12)
at Object.updateContainer (node_modules/react-dom/cjs/react-dom.development.js:12765:14)
at ReactRoot.Object.<anonymous>.ReactRoot.render (node_modules/react-dom/cjs/react-dom.development.js:16069:15)
at node_modules/react-dom/cjs/react-dom.development.js:16488:14
at Object.unbatchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:12557:12)
at legacyRenderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:16484:17)
at Object.render (node_modules/react-dom/cjs/react-dom.development.js:16543:12)
at Object.render (node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:218:50)
at new ReactWrapper (node_modules/enzyme/build/ReactWrapper.js:98:16)
at mount (node_modules/enzyme/build/mount.js:19:10)
at Object.it (src/tests/components/Main.test.js:29:42)
at new Promise (<anonymous>)
at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:118:7)
Is it so hard to test nested connected components? I have to go and use more of it going forward in the application as well. And also does this mean that testing button clicks and all is harder to test as well?
Based on what you are trying to test, you should not have to deal with redux at all.
Since you are testing your <Main /> component and whether it renders the header properly, just mock your <Container /> completely:
jest.mock('../../components/Container', () => ()=> <div id="mockContainer">
mockContainer
</div>);
Then you can shallow mount your <Main /> without worrying about redux.
After that, when you start unit testing your <Container />, just unit test the React.Component class and do not bother with the connected Component, testing it is redundant.
In other words, you should be able to test any React app without ever having to mock the redux store. Just mock connected components entirely.
More info here