i've been having troble getting my nextjs app to work with getServerSideProps() for server-side-rednering. i tried implemening next-redux-wrapper but the state is empty.
*note: redux works fine while it was running on client side, but now im trying to get the state in getServerSideProps() and pass it into the component, so it renders on the server.
store.js:
const reducer = combineReducers({
productList: productListReducer,
categoryList: categoryListReducer,
})
const middleware = [thunk]
const makeStore = context => createStore(reducer, composeWithDevTools(applyMiddleware(...middleware)))
const wrapper = createWrapper(makeStore, {debug: true})
export default wrapper
reducer.js:
export const productListReducer = (state = { products: [] }, action) => {
switch (action.type) {
case HYDRATE:
return {...state, ...action.payload}
case 'PRODUCT_LIST_REQUEST':
return { loading: true, products: [] }
case 'PRODUCT_LIST_SUCCESS':
return { loading: false, products: action.payload }
case 'PRODUCT_LIST_FAIL':
return { loading: false, error: action.payload }
default:
return state
}
}
_app.js:
import wrapper from '../redux/store'
function MyApp({ Component, pageProps }) {
return (
<Component {...pageProps} />
)
}
export default wrapper.withRedux(MyApp)
index.js:
import wrapper from '../redux/store'
export const getServerSideProps = wrapper.getServerSideProps(store => ({req, res}) => {
const state = store.getState()
const { products } = state.productList
return {props: {products: products}}
})
export default function Home({products}) {
return (
<>
<div>{products}</div>
</>
)
}
page.js:
const mapDispatchToProps = (dispatch) => {
return {
listProducts: bindActionCreators(listProducts, dispatch),
listCategories: bindActionCreators(listCategories, dispatch)
}
}
function mapStateToProps(state) {
return {
products: state.productList.products,
loading: state.productList.loading,
categories: state.categoryList.categories,
loadingCategory: state.categoryList.loading
}
}
CategoryScreen.getInitialProps = wrapper.getInitialPageProps((store) => async () => {
await store.dispatch(listProducts())
await store.dispatch(listCategories())
})
export default connect(mapStateToProps, mapDispatchToProps)(CategoryScreen)
reducer.js:
import { HYDRATE } from "next-redux-wrapper"
export const categoryListReducer = (state = { categories: [] }, action) => {
switch (action.type) {
case HYDRATE:
return {...state, ...action.payload}
case 'CATEGORY_LIST_REQUEST':
return { loading: true, categories: [] }
case 'CATEGORY_LIST_SUCCESS':
return { loading: false, categories: action.payload }
case 'CATEGORY_LIST_FAIL':
return { loading: false, error: action.payload }
default:
return state
}
}
store.js:
const combinedReducer = combineReducers({
productList: productListReducer,
categoryList: categoryListReducer
})
const reducers = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state,
...action.payload
}
return nextState
} else {
return combinedReducer(state, action)
}
}
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension')
return composeWithDevTools(applyMiddleware(...middleware))
}
return applyMiddleware(...middleware)
}
export const makeStore = (context) => {
const store = createStore(reducers, bindMiddleware([thunk]))
return store
}
export const wrapper = createWrapper(makeStore, { debug: true })
Related
The totalItems returns value inside the useEffect on first render but returns undefined when refreshed making setPageCounter return undefined. Why is this happening and what is the solution?
const Products = () => {
const dispatch = useDispatch();
const { products, loading, totalItems } = useSelector(
(state) => state.products
);
const [searchParams, setSearchParams] = useSearchParams();
const params = Object.fromEntries([...searchParams]);
console.log(totalItems); // Returns the value after refreshing the page
// Pagination
const [currentPage, setCurrentPage] = useState(1);
const [pageCounter, setPageCounter] = useState(0);
const itemsPerPage = 8;
useEffect(() => {
if (Object.keys(params).length > 0) {
dispatch(searchProduct(params));
} else {
dispatch(getProduct(currentPage));
console.log(totalItems); // Returns undefined after refreshing the page
}
}, [dispatch, currentPage]);
};
Here is the Product Reducer code
import {
ALL_PRODUCT_REQUEST,
ALL_PRODUCT_SUCCESS,
ALL_PRODUCT_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
CLEAR_ERRORS,
} from "../constants/productConstants";
export const productReducer = (state = { products: [] }, action) => {
switch (action.type) {
case ALL_PRODUCT_REQUEST: return {
...state, loading: true, products: []
}
case ALL_PRODUCT_SUCCESS: return {
...state,
loading: false,
products: action.payload.data.data,
perPage: action.payload.data.productsperpage,
pageNumber: action.payload.data.pageNumber,
totalItems: action.payload.data.totalItems
}
case ALL_PRODUCT_FAIL: return {
...state,
loading: false,
error: action.payload
}
case CLEAR_ERRORS: return {
...state,
error: null
}
default: return state;
}
};
Here is the Product Action code
import axios from 'axios';
import {
ALL_PRODUCT_REQUEST,
ALL_PRODUCT_SUCCESS,
ALL_PRODUCT_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
CLEAR_ERRORS,
} from '../constants/productConstants';
export const getProduct = (page) => async (disptach) => {
try {
disptach({ type: ALL_PRODUCT_REQUEST });
const data = await axios.get(`/api/products?page=${page}`);
disptach({
type: ALL_PRODUCT_SUCCESS,
payload: data,
});
} catch (error) {
disptach({
type: ALL_PRODUCT_FAIL,
payload: error.response.data.message,
});
}
}
The component does not re render after successfully update state in redux
i have tried to do some condition in componentShouldUpdate end up with loading true without change
reducer.js
import * as types from "./actionsType";
const INITIAL_STATE = {
slide_data: [],
error: null,
loading: false,
};
const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties,
};
};
const slideReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.SLIDES_FETCH_START:
return updateObject(state, {
error: null,
loading: true,
});
case types.SLIDES_FETCH_SUCSSES:
return updateObject(state, {
slide_data: action.payload,
error: null,
loading: false,
});
case types.SLIDES_FETCH_FAIL:
return updateObject(state, {
error: action.error,
loading: false,
});
default:
return state;
}
};
export default slideReducer;
actions.js
import * as types from "./actionsType";
import axios from "axios";
import { selectSlides } from "./slides.selectors";
export const slidesStart = () => {
return {
type: types.SLIDES_FETCH_START,
};
};
export const slidesSucces = (slides) => {
return {
type: types.SLIDES_FETCH_SUCSSES,
payload: slides,
};
};
export const slidesFail = (error) => {
return {
type: types.SLIDES_FETCH_FAIL,
error: error,
};
};
export const fetchSlides = () => {
return (dispatch) => {
console.log("fetch Start");
dispatch(slidesStart());
axios
.get("http://127.0.0.1:8000/slides/", {
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
dispatch(slidesSucces(res.data));
})
.catch((err) => dispatch(slidesFail(err)));
};
};
component
class IntroPage extends Component {
constructor(props) {
super(props);
this.tlitRef = React.createRef();
this.titlelRef = React.createRef();
this.subTitleRef = React.createRef();
this.showcase = React.createRef();
}
componentDidMount() {
this.props.fetchSlides();
}
render() {
const { slides, loading } = this.props;
if (loading) {
return <h1>Loading</h1>;
}
return (
<div className="intro">
<div className="wrapper">
{slides.map((data) => (
<SwiperSlides data={data} key={data.name} />
))}
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.slides.loading,
error: state.slides.error,
slides: state.slides.slide_data,
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchSlides: () => dispatch(fetchSlides()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(IntroPage);
Register the redux-logger correctly. The data was returned but nothing changes when I do redux-persist and try to reload data come through
update ::
when I change the size of the browser data it correctly appears what is this !!
update : this problem related to swiperjs
and the solution will be like that:
1 - assign swiper instance to React.CreateRef(null) : this.swiper = React.CreateRef(null)
2 - in componentDidUpdate() make a swiper update : this.swiper.current.update()
4 - use a arrow function syntax in swiper on functions to refer to the outer scope
when i using redux in my react app,reducer just return initial state for me.which is always returned the false here.thanks for your answer.
[UPDATE]:
i chaned
let newState = state
to this:
let newState = {...state}
but also reducer returned false
this my reducer:
const initialState = {
modalVisible: false
};
function modalReducer(state = initialState, action) {
let newState = {...state};
switch (action.type) {
case "SHOW":
newState.modalVisible = true;
console.log("Show!");
break;
case "HIDE":
newState.modalVisible = false;
console.log("Hide!");
break;
}
return newState;
}
export default modalReducer;
and this is my component (Svg Viewer component)
import React from "react";
import { connect } from "react-redux";
const SvgViewer = ({
nodesData,
svgFilePath,
modalVisible,
onModalShow,
onModalHide
}) => {
const clickHandler = () => {
onModalShow();
console.log(modalVisible); //return false
onModalHide();
console.log(modalVisible);
};
return (
<div className="unit-schema-container1" key={svgFilePath}>
<object id="svgobject" type="image/svg+xml" data={svgFilePath}></object>
<button onClick={clickHandler}></button>
</div>
);
};
const mapStateToProps = state => {
return { modalVisible: state.modalVisible };
};
const mapDispatchToProps = dispatch => {
return {
onModalShow: () => {
dispatch({ type: "SHOW" });
},
onModalHide: () => {
dispatch({ type: "HIDE" });
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(SvgViewer);
You should try using this. Its easier to read and uses the best practices
const initialState = {
modalVisible: false
};
function modalReducer(state = initialState, action) {
switch (action.type) {
case "SHOW":
return {...state, modalVisible: true }
case "HIDE":
return {...state, modalVisible: false }
default:
return state
}
}
you should return a new state object
let newState = {...state}
I think the problem is in mapStateToprops function, please post your main reducer,
try to edit you mapStateToProps
const mapStateToProps = state => {
return { modalVisible: state.modalVisible.modalVisible };
};
I have a problem with Redux doesn't update the state. Component gets right initial state. Action is dispatched right, data is fetched right and is accesible in action payload inside reducer. Reducer is executing, right case in switch is picked. Just new state doesn't appear in component. I have three others components where it works just fine, only this one cracks.
component
import fetchLinksPage from '../state/links/actions'
...
let Links = ({linksPageLoaded, linksPage, fetchLinksPage}) => {
useEffect( () => {
if(!linksPageLoaded) {
fetchLinksPage()
console.log(linksPage)
}
},[])
return ( ... )
}
const mapStateToProps = ({linksPageReducer}) => {
return linksPageReducer
}
const mapDispatchToProps = dispatch => {
return {
fetchLinksPage: () => dispatch(fetchLinksPage())
}
}
Links = connect(mapStateToProps, mapDispatchToProps)(Links)
actions
// action types
export const GET_LINKS_PAGE = 'GETLINKSPAGE'
export const LINKS_PAGE_LOADED = 'LINKSPAGELOADED'
export const LINKS_PAGE_ERROR = 'LINKSPAGEERROR'
// action creators
export const getLinksPage = () => {
return {
type: GET_LINKS_PAGE
}
}
export const linksPageLoaded = (data) => {
return {
type: LINKS_PAGE_LOADED,
payload: data
}
}
export const linksPageError = (error) => {
return {
type: LINKS_PAGE_ERROR,
payload: error
}
}
const fetchLinksPage = () => {
return dispatch => {
dispatch(getLinksPage())
fetch('http://portfolio.adamzajac.info/_/items/links?fields=*,logo.*.*')
.then(response => response.json())
.then(data => {
dispatch(linksPageLoaded(data.data))
})
.catch( error => {
dispatch(linksPageError(error))
})
}
}
export default fetchLinksPage
reducer
import * as actions from './actions.js'
const linksPageReducer = (state={}, action) => {
switch (action.type) {
case actions.GET_LINKS_PAGE:
return { ...state, linksPageLoading: true }
case actions.LINKS_PAGE_LOADED:
//console.log('update state')
return { ...state, linksPage: action.payload, linksPageLoading: false, linksPageLoaded: true }
case actions.LINKS_PAGE_ERROR:
return { ...state, linksPageError: action.payload, linksPageLoading: false}
default:
return { ...state, linksPageLoading: false, linksPageLoaded: false, linksPage:[], linksPageError:''}
}
}
export default linksPageReducer
store
import aboutPageReducer from './state/about/reducer'
import projectsPageReducer from './state/projects/reducer'
import skillsPageReducer from './state/skills/reducer'
import linksPageReducer from './state/links/reducer'
const rootReducer = combineReducers({
aboutPageReducer,
projectsPageReducer,
skillsPageReducer,
linksPageReducer
})
const store = createStore(
rootReducer,
applyMiddleware(thunk)
)
How can I fetch only one data and write it to Header ?
I am using firebase and react-redux.
firebase structure i try to write "organization": inovanka:
Action File Codes:
import firebase from 'firebase';
import { Actions } from 'react-native-router-flux';
import { ORGANIZATION_NAME_DATA_SUCCESS } from './types';
export const organizationName = () => {
const { currentUser } = firebase.auth();
return (dispatch) => {
firebase.database().ref(`/organizations/${currentUser.uid}`)
.on('value', snapshot => {
dispatch({ type: ORGANIZATION_NAME_DATA_SUCCESS, payload: snapshot.val() });
});
};
}
Reducer File :
import { ORGANIZATION_NAME_DATA_SUCCESS } from '../actions/types';
const INITIAL_STATE = {
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case ORGANIZATION_NAME_DATA_SUCCESS:
console.log(action); // data retrieved as array
return action.payload
default:
return state;
}
};
Component: (I would like to write it to this)
class HomePage extends Component {
componentWillMount() {
}
render() {
return (
<Container>
<Header>
<Text> i would like to write it here </Text>
</Header>
<Content>
</Content>
</Container>
);
}
}
const mapStateToProps = ({ homepageResponse }) => {
const organizationArray = _.map(homepageResponse, (val, uid) => {
return { ...val, uid }; //
});
return { organizationArray };
};
export default connect(mapStateToProps, { organizationName })(HomePage);
Change this:
firebase.database().ref(`/organizations/${currentUser.uid}`)
.on('value', snapshot => {
to this:
firebase.database().ref(`/organizations/${currentUser.uid}`)
.once('value', snapshot => {
using once() will read data only one time, thus fetching only one data
Solution is Here !
Action File:
export const organizationName = () => {
const { currentUser } = firebase.auth();
return (dispatch) => {
firebase.database().ref(`/organizations/${currentUser.uid}`)
.once('value', snapshot => {
_.mapValues(snapshot.val(), o => {
console.log(o);
dispatch({ type: ORGANIZATION_NAME_DATA_SUCCESS, payload: {organization: o.organization, fullname: o.fullname }});
});
});
};
}
Reducer File
const INITIAL_STATE = {
organization: '',
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case ORGANIZATION_NAME_DATA_SUCCESS:
console.log(action);
return {...state, organization:action.payload.organization };
default:
return state;
}
};
Component File MapToStateProps and componentWillMount
const mapStateToProps = state => {
const { organization, fullname } = state.homepageResponse;
console.log("burada" + organization);
return { organization, fullname };
};
componentWillMount(){
this.props.organizationName();
}
*Last Step Header *
render() {
return (
<Container>
<Header>
<Text> { this.props.organization } </Text>
</Header>
</Container>
}
Thank You Everyone