Next.js + Redux server side rendering: Has data, but doesn't render on server side - server-side-rendering

I'm trying to add redux integration to my Next.js app, but I can't get serverside rendering working the way it should. I based my implementation off the official nextjs redux example.
In the end, when the page comes back from the server, the data is present as JSON data in the output, but the actual rendering based on this data did not happen. The weird thing is that before I used redux, the content DID render the way it should.
Naturally, I'm also getting React's checksum warning, indicating that the markup on the server is different.
I have no idea how to make this work properly on the server side. Is there something that I'm missing?
Here's the HTML generated by Next.js:
<h1 data-reactid="3">Test page</h1>
</div></div></div><div id="__next-error"></div></div><div><script>
__NEXT_DATA__ = {"props":{"isServer":true,"store":{},
"initialState":{"authors":{"loading":false,"items":{"4nRpnr66B2CcQ4wsY04CIQ":… }
,"initialProps":{}},"pathname":"/test","query":{},"buildId":1504364251326,"buildStats":null,"assetPrefix":"","nextExport":false,"err":null,"chunks":[]}
module={}
__NEXT_LOADED_PAGES__ = []
__NEXT_LOADED_CHUNKS__ = []
__NEXT_REGISTER_PAGE = function (route, fn) {
__NEXT_LOADED_PAGES__.push({ route: route, fn: fn })
}
__NEXT_REGISTER_CHUNK = function (chunkName, fn) {
__NEXT_LOADED_CHUNKS__.push({ chunkName: chunkName, fn: fn })
}
</script><script async="" id="__NEXT_PAGE__/test" type="text/javascript" src="/_next/1504364251326/page/test"></script><script async="" id="__NEXT_PAGE__/_error" type="text/javascript" src="/_next/1504364251326/page/_error/index.js"></script><div></div><script type="text/javascript" src="/_next/1504364251326/manifest.js"></script><script type="text/javascript" src="/_next/1504364251326/commons.js"></script><script type="text/javascript" src="/_next/1504364251326/main.js"></script></div></body></html>
AS you can see, the initialState value is populated, it contains all the required data, but the DOM still shows empty!.
If I render the dom on the client side, the js picks up the initial content and rerenders the page with the loaded content in place.
Here's my test page JS file:
import React from 'react'
import map from 'lodash.map';
import { initStore } from '../lib/store';
import * as actions from '../lib/actions';
import withRedux from 'next-redux-wrapper';
class IndexPage extends React.PureComponent {
static getInitialProps = ({ store, req }) => Promise.all([
store.dispatch(actions.fetchAll)
]).then( () => ({}) )
render() {
const latestPlants = this.props.plants.latest || [];
return (
<div>
<h1>Test page</h1>
{ map(this.props.plants.items, p => (
<div>{p.fields.name}</div>
))}
</div>
)
}
}
export default withRedux(initStore, data => data, null)(IndexPage)
For whatever it's worth, here's the action that I call above:
export const fetchAll = dispatch => {
dispatch({
type: LOADING_ALL
})
return axios.get('/api/frontpage')
.then( response => {
const data = response.data
dispatch({
type: RESET_AUTHORS,
payload: data.authors
})
dispatch({
type: RESET_PLANTS,
payload: data.plants
})
dispatch({
type: RESET_POSTS,
payload: data.posts
})
});
}
Any help with this would be greatly appreciated, I'm at a loss on how to make this work as expected. Anyone have any leads? Please also comment if there's something I can clarify.

I recommend to split the code in different parts. First, I'll create a store, with something like this:
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import reducer from './reducers'
export const initStore = (initialState = {}) => {
return createStore(reducer, initialState, applyMiddleware(thunkMiddleware))
}
Then I'll create the store with the types to handle:
const initialState = {
authors: null,
plants: null,
posts: null
}
export default (state = initialState, action) => {
switch (action.type) {
case 'RESET':
return Object.assign({}, state, {
authors: action.authors,
plants: action.plants,
posts: action.posts
})
default:
return state
}
}
In the actions I'll have something like this:
export const fetchAll = dispatch => {
return axios.get('/api/frontpage')
.then( response => {
const data = response.data
dispatch({
type: 'RESET',
authors: data.authors,
plants: data.plants,
posts: data.posts
})
});
}
The index will be something like this:
import React from 'react'
import { initStore } from '../store'
import withRedux from 'next-redux-wrapper'
import Main from '../components'
class Example extends React.Component {
render() {
return (
<div>
<Main />
</div>
)
}
}
export default withRedux(initStore, null)(Example)
And the component Main:
import React, {Component} from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { fetchAll } from '../../actions'
class Data extends Component {
componentWillMount() {
this.props.fetchAll()
}
render() {
const { state } = this.props
return (
<div>
<h1>Test page</h1>
{ map(state.plants.items, p => (
<div>{p.fields.name}</div>
))}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
state
}
}
const mapDistpatchToProps = dispatch => {
return {
fetchAll: bindActionCreators(fetchAll, dispatch)
}
}
export default connect(mapStateToProps, mapDistpatchToProps)(Data)
Make the changes for what you need.
You can check some full examples here:
Form handler
Server Auth

Related

Nextjs dynamic routes with next-i18next build error

I have an edit page that will be rendered with an id parameter and it works fine when application is running but while building the nextjs app I get this error
[Error: ENOENT: no such file or directory, rename 'C:\Users\Ahsan Nisar\Documents\GitHub\customer-portal\frontend.next\export\en\companies\edit[id].html' -> 'C:\Users\Ahsan Nisar\Documents\GitHub\customer-portal\frontend.next\server\pages\en\companies\edit[id].html']
the full error
I am not sure what this error is related to or what mistake am I making in my code that this error is occuring during build time.
Here is the code of my page
import { WithAuthorization } from 'common/roq-hocs';
import { MainLayout } from 'layouts';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import React, { FunctionComponent } from 'react';
import { CompaniesEditView } from 'views/companies-edit';
const CompanyCreatePage: FunctionComponent = () => {
const { t } = useTranslation('companiesEdit');
return (
<MainLayout title={t('title')}>
<WithAuthorization
permissionKey="companies.update"
failComponent={
<div className="mt-16 text-2xl text-center text-gray-600">
<span>{t('noView')}</span>
</div>
}
>
<CompaniesEditView />
</WithAuthorization>
</MainLayout>
);
};
export const getStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, ['common', 'companiesEdit'])),
},
});
export const getStaticPaths = () => ({
paths: ['/companies/edit/[id]'],
fallback: true,
});
export default CompanyCreatePage;
I think that the problem might be that you are not returning the expected paths model in getStaticPaths function.
Minimal example of this page:
import { GetStaticPaths, GetStaticProps } from 'next';
import { useRouter } from 'next/router';
const CompanyCreatePage = () => {
const router = useRouter();
const { id } = router.query;
return (
<div>
<h1>Company Create Page Content for id: {id}</h1>
</div>
);
};
export const getStaticPaths: GetStaticPaths = async () => {
// Get all possible 'id' values via API, file, etc.
const ids = ['1', '2', '3', '4', '5']; // Example
const paths = ids.map(id => ({
params: { id },
}));
return { paths, fallback: false };
};
export const getStaticProps: GetStaticProps = async context => {
return { props: {} };
};
export default CompanyCreatePage;
Then, navigating to the page /users/edit/3/ returns the following content
Take into account that the fallback param in getStaticPaths changes the behavior of getStaticProps function. For reference, see the documentation

Why Redux action is not Being being dispatched in Redux-Tooklit

I am using react-redux with redux and redux-toolkit. And according to this example, i created an async dispatch that calls the reducer action when resolved.
import { createSlice } from "#reduxjs/toolkit";
import axios from "axios";
export const BlogSlice = createSlice({
name: "Blog",
initialState: {
BlogList: null,
},
reducers: {
getBlogList: (state, action) => {
console.log(action.payload);
state.BlogList = action.payload;
}
},
});
export const { getBlogList } = BlogSlice.actions;
export const getBlogListAsync = (user_id) => (dispatch) => {
axios.get(`/api/blog/getblogs/${user_id}`).then((res) => {
console.log(res.data);
dispatch(getBlogList(res.data.result));
});
};
export const selectBlogList = (state) => state.Blog.BlogList;
export default BlogSlice.reducer;
I have used it in a component accordingly so that, the component dispatches getBlogListAsync and that logs the res.data but getBlogList is not being dispatched. I tried putting other console.log() but don't understand what is wrong.
A similar Slice is working perfectly with another Component.
It is hard to say for sure what's wrong here because there is nothing that is definitely wrong.
res.data.result?
You are logging res.data and then setting the blog list to res.data.result. My best guess as to your mistake is that res.data.result is not the the correct property for accessing the blogs, but I can't possibly know that without seeing your API.
console.log(res.data);
dispatch(getBlogList(res.data.result));
missing middleware?
Is there any chance that "thunk" middleware is not installed? If you are using Redux Toolkit and omitting the middleware entirely, then the thunk middleware will be installed by default. Also if this were the case you should be getting obvious errors, not just nothing happening.
it seems fine...
I tested out your code with a placeholder API and I was able to get it working properly. Maybe this code helps you identify the problem on your end. Code Sandbox Demo.
import React from "react";
import { createSlice, configureStore } from "#reduxjs/toolkit";
import axios from "axios";
import { Provider, useDispatch, useSelector } from "react-redux";
export const BlogSlice = createSlice({
name: "Blog",
initialState: {
BlogList: null
},
reducers: {
getBlogList: (state, action) => {
console.log(action.payload);
state.BlogList = action.payload;
}
}
});
export const { getBlogList } = BlogSlice.actions;
const store = configureStore({
reducer: {
Blog: BlogSlice.reducer
}
});
export const getBlogListAsync = (user_id) => (
dispatch: Dispatch
) => {
// your url `/api/blog/getblogs/${user_id}`
const url = `https://jsonplaceholder.typicode.com/posts?userId=${user_id}`; // placeholder URL
axios.get(url).then((res) => {
console.log(res.data);
// your list: res.data.result <-- double check this
const list = res.data; // placeholder list
dispatch(getBlogList(list));
});
};
export const selectBlogList = (state) => state.Blog.BlogList;
const Test = () => {
const dispatch = useDispatch();
const blogs = useSelector(selectBlogList);
const user_id = "1";
return (
<div>
<button onClick={() => dispatch(getBlogListAsync(user_id))}>
Load Blogs
</button>
<h3>Blog Data</h3>
<div>{JSON.stringify(blogs)}</div>
</div>
);
};
export default function App() {
return (
<Provider store={store}>
<Test />
</Provider>
);
}

Redux Thunk not dispatching

I have installed Redux Thunk on my application and it's been working fine so far, all of the previous actions I've created are pulling out data from APIs successfully, however the following action is not even dispatching actions to my reducer, any idea what am I missing?
// my action
export const fetchClub = id => {
debugger
return (dispatch) => {
if (id){
dispatch ({type: 'START_PULLING_NIGHTCLUB'});
let targetUrl = `http://localhost:3001/nightclub`
fetch(targetUrl)
.then(res => {
debugger
return res.json()
})
.then(nightclub => dispatch({type: 'CURRENT_NIGHTCLUB', nightclubs: nightclub.result}))
.catch(error => {
console.log(error)
})
}}}
//my reducer
import {combineReducers} from "redux"
const rootReducer = combineReducers({
nightclubs: nightClubsReducer,
user: userReducer
})
export default rootReducer
function nightClubsReducer(state = {}, action) {
debugger
switch (action.type){
case 'ADD_NIGHTCLUBS':
debugger
let nightclubs = action.nightclubs
// filering the results just to show nightclubs rather than hotels
nightclubs = nightclubs.filter( function (nightclub){
return !nightclub.types.includes("lodging")
})
return {...state.nightclubs, nightclubs}
case 'CURRENT_NIGHTCLUB':
debugger
let nightclub = action.nightclub
return {...state.nightclubs, nightclub}
default:
return state
}}
function userReducer(state = {user: {logged_in: false}}, action){
let current_user = {}
switch (action.type){
case 'ADD_USER_LOCATION':
let coords = action.location.coords
return {...state.user, coords}
case 'CREATE_USER':
current_user = action.user
state.logged_in = true
return {...state.user, current_user}
case 'ADD_LOGGED_IN_USER':
current_user = action.user
if(state.user){
state.user.logged_in = action.user.logged_in}
return {...state.user, current_user}
default:
return state
}
}
I should be hitting the debugger on the first line of my nightClubsReducer however nothing happens.
My Nightclub component is connected properly as far as I'm aware:
import React, { Component } from 'react';
import Maya from '../assets/Mayaclubbio.jpg'
import '../NightClubPage.css'
import { connect } from 'react-redux';
import { fetchClub } from '../actions/NightClubs';
class NightClub extends Component {
constructor(props) {
super(props);
this.id = props.match.params.id
}
componentDidMount() {
fetchClub(this.id)
}
render() {
debugger
return (
<React.Fragment>
//HTML code
</React.Fragment>
)
}
}
const mapStateToProps = (state) => {
debugger
return {
nightclub: state.nightclubs.nightclub,
user: state.user
}
}
export default connect(mapStateToProps, { fetchClub })(NightClub);
I have no clue what could be failing as I'm using the same logic for the rest of my actions and they are working just fine.
I think calling the action from props should fix your issue
componentDidMount() {
this.props.fetchClub(this.id);
}

"Cannot read property of undefined" being thrown by react-apollo used with React-NextJS

My NextJS project has been giving me grief over Graph QL these days. I've been trying to implement an Apollo client solution to retrieve data from a remote GraphQL server into a custom component. But no matter which solution I try, I always end up with this error. Here's my current react-apollo implementation:
// /lib/with-apollo-client.js
import React from "react";
import Head from "next/head";
import { getDataFromTree } from "react-apollo";
import initApollo from "./init-apollo";
export default App => {
return class WithData extends React.Component {
static displayName = `WithData(${App.displayName})`;
static async getInitialProps(ctx) {
const { Component, router } = ctx;
const apollo = initApollo({});
ctx.ctx.apolloClient = apollo;
let appProps = {};
if (App.getInitialProps) {
appProps = await App.getInitialProps(ctx);
}
// Run all GraphQL queries in the component tree
// and extract the resulting data
if (!process.browser) {
try {
// Run all GraphQL queries
await getDataFromTree(
<App
{...appProps}
Component={Component}
router={router}
apolloClient={apollo}
/>
);
} catch (error) {
console.error("Error while running `getDataFromTree`", error);
}
// getDataFromTree does not call componentWillUnmount
// head side effect therefore need to be cleared manually
Head.rewind();
}
// Extract query data from the Apollo store
const apolloState = apollo.cache.extract();
return {
...appProps,
apolloState
};
}
constructor(props) {
super(props);
this.apolloClient = initApollo(props.apolloState);
}
render() {
return <App {...this.props} apolloClient={this.apolloClient} />;
}
};
};
// /lib/init-apollo.js
import { ApolloClient, InMemoryCache, HttpLink } from 'apollo-boost'
import fetch from 'isomorphic-unfetch'
let apolloClient = null
// Polyfill fetch() on the server (used by apollo-client)
if (!process.browser) {
global.fetch = fetch
}
function create (initialState) {
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
link: new HttpLink({
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
credentials: 'same-origin' // Additional fetch() options like `credentials` or `headers`
}),
cache: new InMemoryCache().restore(initialState || {})
})
}
export default function initApollo (initialState) {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (!process.browser) {
return create(initialState)
}
// Reuse client on the client-side
if (!apolloClient) {
apolloClient = create(initialState)
}
return apolloClient
}
The component I'm retrieving data into looks like this:
// /components/PostsList2.jsx
import { Query } from 'react-apollo'
import gql from 'graphql-tag'
export const allUsersQuery = gql`
query allUsers($first: Int!, $skip: Int!) {
allUsers(orderBy: createdAt_DESC, first: $first, skip: $skip) {
id
firstName
createdAt
}
_allUsersMeta {
count
}
}
`
export const allUsersQueryVars = {
skip: 0,
first: 10
}
export default function PostsList2 () {
return (
<Query query={allUsersQuery} variables={allUsersQueryVars}>
{({ loading, error, data: { allUsers, _allUsersMeta }, fetchMore }) => {
if (error) return <aside>Error loading users!</aside>
if (loading) return <div>Loading</div>
const areMorePosts = allUsers.length < _allUsersMeta.count
return (
<section>
<ul>
{allUsers.map((user, index) => (
<li key={user.id}>
<div>
<span>{index + 1}. </span>
<div>{user.firstName}</div>
</div>
</li>
))}
</ul>
{areMorePosts ? (
<button onClick={() => loadMorePosts(allUsers, fetchMore)}>
{' '}
{loading ? 'Loading...' : 'Show More'}{' '}
</button>
) : (
''
)}
</section>
)
}}
</Query>
)
}
function loadMorePosts (allUsers, fetchMore) {
fetchMore({
variables: {
skip: allUsers.length
},
updateQuery: (previousResult, { fetchMoreResult }) => {
if (!fetchMoreResult) {
return previousResult
}
return Object.assign({}, previousResult, {
// Append the new users results to the old one
allUsers: [...previousResult.allUsers, ...fetchMoreResult.allUsers]
})
}
})
}
Since this is a NextJS project, there's also an _app.jsx that I've wrapped in a special provider component:
// /pages._app.jsx
/* eslint-disable max-len */
import '../static/styles/fonts.scss';
import '../static/styles/style.scss';
import '../static/styles/some.css';
import CssBaseline from '#material-ui/core/CssBaseline';
import { ThemeProvider } from '#material-ui/styles';
import jwt from 'jsonwebtoken';
import withRedux from 'next-redux-wrapper';
import App, {
Container,
} from 'next/app';
import Head from 'next/head';
import React from 'react';
import { Provider } from 'react-redux';
import makeStore from '../reducers';
import mainTheme from '../themes/main-theme';
import getSessIDFromCookies from '../utils/get-sessid-from-cookies';
import getLanguageFromCookies from '../utils/get-language-from-cookies';
import getUserTokenFromCookies from '../utils/get-user-token-from-cookies';
import removeFbHash from '../utils/remove-fb-hash';
import withApolloClient from '../lib/with-apollo-client'
import { ApolloProvider } from 'react-apollo'
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
let userToken;
let sessID;
let language;
if (ctx.isServer) {
ctx.store.dispatch({ type: 'UPDATEIP', payload: ctx.req.headers['x-real-ip'] });
userToken = getUserTokenFromCookies(ctx.req);
sessID = getSessIDFromCookies(ctx.req);
language = getLanguageFromCookies(ctx.req);
const dictionary = require(`../dictionaries/${language}`);
ctx.store.dispatch({ type: 'SETLANGUAGE', payload: dictionary });
if(ctx.res) {
if(ctx.res.locals) {
if(!ctx.res.locals.authenticated) {
userToken = null;
sessID = null;
}
}
}
if (userToken && sessID) { // TBD: validate integrity of sessID
const userInfo = jwt.verify(userToken, process.env.JWT_SECRET);
ctx.store.dispatch({ type: 'ADDUSERINFO', payload: userInfo });
}
ctx.store.dispatch({ type: 'ADDSESSION', payload: sessID }); // component will be able to read from store's state when rendered
}
const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};
return { pageProps };
}
componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentNode.removeChild(jssStyles);
}
// Register serviceWorker
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/serviceWorker.js'); }
// Handle FB's ugly redirect URL hash
removeFbHash(window, document);
}
render() {
const { Component, pageProps, store, apolloClient } = this.props;
return (
<Container>
<Head>
// redacted for brevity
</Head>
<ThemeProvider theme={mainTheme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<ApolloProvider client={apolloClient}>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</ApolloProvider>
</ThemeProvider>
</Container>
);
}
}
export default withApolloClient(withRedux(makeStore)(MyApp));
So with this setup, when I compile and run my app, it throws the following:
TypeError: Cannot read property 'allUsers' of undefined
I'm really lost! The repo is up at https://github.com/amitschandillia/proost/tree/master/web.

Redux action is not being fired

Redux action changePictogramsKeyword is not being fired.
This is the file where I define my action and reducer (redux/module/keyword.js):
export const CHANGE_PICTOGRAMS_KEYWORD = 'CHANGE_PICTOGRAMS_KEYWORD'
export function changePictogramsKeyword (keyword) {
return {
type: CHANGE_PICTOGRAMS_KEYWORD,
keyword
}
}
// Updates error message to notify about the failed fetches.
export default function pictogramsKeyword (state = '', action) {
switch (action.type) {
case CHANGE_PICTOGRAMS_KEYWORD:
return action.keyword
default:
return state
}
}
My root reducer:
import { combineReducers } from 'redux'
import { routerReducer as router } from 'react-router-redux'
import locale from './modules/locale'
import errorMessage from './modules/error'
import pictogramsKeyword from './modules/keyword'
export default combineReducers({
locale,
router,
pictogramsKeyword,
errorMessage
})
So with the devTools I can check that my initialState is as I expected from the rootReducer:
locale:"en"
router:{} 1 key
pictogramsKeyword:""
errorMessage:null
This is the code of the view where I connect to Redux Store. Component SearchBox is in charge of firing the action changePictogramsKeyword:
import React, {Component, PropTypes} from 'react'
import SearchBox from 'components/SearchBox.js'
import { connect } from 'react-redux'
import { changePictogramsKeyword } from 'redux/modules/keyword'
class SearchPictogramsView extends Component {
handleDismissClick (e) {
this.props.resetErrorMessage()
e.preventDefault()
}
render () {
const { children, inputValue } = this.props
return (
<div>
<SearchBox value={inputValue} onChange={changePictogramsKeyword} />
{children}
</div>
)
}
}
SearchPictogramsView.propTypes = {
inputValue: PropTypes.string.isRequired,
children: PropTypes.node
}
function mapStateToProps (state, ownProps) {
return {
errorMessage: state.errorMessage,
inputValue: state.pictogramsKeyword
}
}
export default connect(mapStateToProps, {
resetErrorMessage, changePictogramsKeyword
})(SearchPictogramsView)
This is the code of the SearchBox component. AutoComplete is a material-ui component. onUpdateInput method gets fired everytime I press a key, however changePictogramsKeyword is not being fired (i see nothing through the dev tools)
import React, {Component, PropTypes} from 'react'
import AutoComplete from 'material-ui/lib/auto-complete'
import RaisedButton from 'material-ui/lib/raised-button'
class SearchBox extends Component {
constructor (props) {
super(props)
this.handleUpdateInput = this.handleUpdateInput.bind(this)
}
handleUpdateInput = (t) => {
console.log(t)
this.props.onChange(t)
}
render () {
return (
<div>
<AutoComplete onUpdateInput={this.handleUpdateInput} searchText={this.props.value} />
</div>
)
}
}
SearchBox.propTypes = {
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired
}
export default SearchBox
Right now, your action only gets called, but not dispatched because you're not mapping the actions correctly in the connect() call. (see the official documentation for more information)
In your SearchPictogramsView, change the mapDispatchToProps function of the connect() call to return an object with the wrapped functions:
export default connect(mapStateToProps, (dispatch) => {
return {
resetErrorMessage: () => dispatch(resetErrorMessage()),
changePictogramsKeyword: () => dispatch(changePictogramsKeyword())
};
})(SearchPictogramsView)
You can clean it up by making mapDispatchToProps its own function too:
function mapDispatchToProps(dispatch) {
return {
resetErrorMessage: () => dispatch(resetErrorMessage()),
changePictogramsKeyword: () => dispatch(changePictogramsKeyword())
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SearchPictogramsView)
Let me know if that works!
It was really in the docs:
If an object is passed, each function inside it will be assumed to be
a Redux action creator. An object with the same function names, but
with every action creator wrapped into a dispatch call so they may be
invoked directly, will be merged into the component’s props
When I wrote:
<SearchBox value={inputValue} onChange={changePictogramsKeyword} />
Now is:
<SearchBox value={inputValue} onChange={this.props.changePictogramsKeyword} />
So I really call the dispatch of the action and not just the action!

Resources