ooade / NextSimpleStarter

:whale: Simple and Accessible PWA boilerplate with Nextjs 12 and MUI
https://nextss.vercel.app
MIT License
939 stars 200 forks source link

How to get the state from the Store #75

Closed michaelmota closed 5 years ago

michaelmota commented 5 years ago

Hello,

I used this boilerplate to migrate a project made with Create-React-App using Redux, and I have to say that it's been a complete headache understanding the SSR with React, but I'm almost done with it. I'm new using NextJS and I want to know how can I get the state from the Store, for example I want to use the URL prop on my ComponentWillMount of Blog.

Here is my store:

import { createStore, compose, applyMiddleware } from 'redux'

import rootReducer from '../reducers'
import { MenuActiveLink } from '../reducers/menuReducer';
import { 
    CurrentAgent, 
    CurrentProperty, 
    CurrentBlogPost 
} from '../reducers/CurrentReducer';

import { 
    loadPropertiesFilters, 
    ChangeListingTypeSearch, 
    clearSegmetation, 
    chargeSegmentationValues
} from '../reducers/PropertiesReducer';

const state = {
    url:'http://localhost:8000/api/v1',
    staticUrl: 'http://localhost:8000',
    mediaUrl: 'http://localhost:8000/media/',
    ListProperties: [],
    currentPage:'inicio',
    PropertiesList:[],
    ListingTypeSearch:'',
    currentProperty: '',
    currentBlogPost: '',
    currentAgent: '',
    segmentation:'',
    segmentationData:'',
}

const enhancers = compose(
    typeof window !== 'undefined' && process.env.NODE_ENV !== 'production'
        ? window.devToolsExtension && window.devToolsExtension()
        : f => f
)

const reducer = (state, action) => {
    switch (action.type) {
        case 'MenuActiveLink':
            return MenuActiveLink(state,action);
        case 'loadPropertiesFilters':
            return loadPropertiesFilters(state,action);
        case 'ChangeListingTypeSearch':
            return ChangeListingTypeSearch(state,action);
        case 'CurrentProperty':
            return CurrentProperty(state,action);
        case 'CurrentBlogPost':
            return CurrentBlogPost(state,action);
        case 'CurrentAgent':
            return CurrentAgent(state,action);
        case 'clearSegmetation':
            return clearSegmetation(state,action);
        case 'chargeSegmentationValues':
            return chargeSegmentationValues(state,action);
        case 'URL':
            return {...state, url: action.payload};
        default:
            break;
    }
    return state;
}

const createStoreWithMiddleware = applyMiddleware()(createStore)

export default initialState =>
    createStoreWithMiddleware(rootReducer, initialState, enhancers, reducer, state)

and here is the component where I want to get the state for the URL for example:

import React, {Component} from 'react';
import HeaderBlue from '../components/Layouts/Header/HeaderBlue'
import Slider from "react-slick";
import {Tabs, Tab} from 'tabler-react';
import {Container, Row, Col} from 'reactstrap';
import {connect} from 'react-redux';
import axios from 'axios';
import withRedux from "next-redux-wrapper";
// Demo Imports
import BlogCard from '../components/Blog/BlogCard';
import BlogSlider from '../components/Blog/BlogSlider'

const getState = (state) => {
    return {
        url: state.url,
    }
}

const Dispatch = (dispatch) =>{
  return{
    loadBlogPost: (id) =>{
      const action ={
        type: "CurrentBlogPost",
        id:id,
      }
      dispatch(action)
    },
  }
}

class Blog extends Component {
    constructor(props) {
        super(props);
        this.state = {
            blogposts: [],
        }
    }

    static async getInitialProps({ store }) {
        // Adding a default/initialState can be done as follows:
        // store.dispatch({ type: 'ADD_TODO', text: 'It works!' });
        console.log(store.state)
        const res = await fetch(
            'https://api.github.com/repos/ooade/NextSimpleStarter'
        )
        const json = await res.json()
        return { stars: json.stargazers_count }
    }

    componentWillMount = () => {
        let url = this.props.url
        axios.get(url + '/blog/')
        .then(response => {
            this.setState({ blogposts: response.data.results })
        })
    }

    getBlogPost = (e) =>{
        let id = e.target.getAttribute('data-id')
        this.props.loadBlogPost(id)
        this.setState({redirect:true})
    }

    render() {
        const settingsMainSlider = {
            dots: false,
            infinite: true,
            speed: 1000,
            slidesToShow: 3,
            slidesToScroll: 3,
            autoplay: true,
            arrows: false,
            responsive: [
                {
                    breakpoint: 1024,
                    settings: {
                        slidesToShow: 1,
                        slidesToScroll: 1,
                        initialSlide: 1
                    }
                },
                {
                    breakpoint: 480,
                    settings: {
                        slidesToShow: 1,
                        slidesToScroll: 1
                    }
                }
            ]
        };
        return (
            <div>
                <HeaderBlue blog="active"/>
                <section id="slider">
                    <Slider {...settingsMainSlider}>
                        {this.state.blogposts.map(e => {
                            return (
                                <BlogSlider 
                                    title={e.title}
                                    blogId={e.id}
                                    imgSrc={e.image}
                                    onClick={this.getBlogPost}
                                    getBlogPost={this.getBlogPost}
                                />
                            )
                        })}
                    </Slider>
                </section>
                <section id="categoryFilter" style={{paddingBottom: '100px'}}>
                    <Tabs initialTab="Portada">
                        <Tab title="Portada">
                            <Container className="pt-4">
                                <div className="blogCategories-container">
                                    <div>Prop from Redux [{this.props.foo}]</div>
                                    <div>Prop from getInitialProps [{this.props.custom}]</div>
                                    <h5 style={{ marginBottom: '0', fontSize: '1.5rem' }}>Tips & Recomendaciones</h5>
                                    <span className="opacity5 text-muted">Ver todas las entradas &raquo;</span>
                                </div>
                                <Row>
                                    {this.state.blogposts.map(e => {
                                        return (
                                            <Col sm={4}>
                                                <BlogCard
                                                    key={e.id}
                                                    title={e.title}
                                                    imgSrc={e.image}
                                                    category={e.category}
                                                    // Prop related
                                                    blogId={e.id}
                                                    onClick={this.getBlogPost}
                                                    getBlogPost={this.getBlogPost}
                                                />
                                            </Col>
                                        )
                                    })}
                                </Row>
                            </Container>
                        </Tab>
                    </Tabs>
                </section>
            </div>
        )
    }
}
export default connect()(Blog);

Any ideas of how to get this done would help me a lot.

ooade commented 5 years ago

With the example I gave here, you could get the state using store.getState();

michaelmota commented 5 years ago

Yes I tried that but how I can make it work correctly, because when I use: store.getState(state.url) it says: "Cannot read property 'url' of undefined", and on the store I'm declaring the state and exporting it.

Again sorry for my ignorance, could you see if I have anything wrong here on the way I'm calling getState?:

static async getInitialProps({ store, state }) {
        store.getState(state.url);
    const res = await fetch(
        'https://api.github.com/repos/ooade/NextSimpleStarter'
    )
    const json = await res.json()
    return { stars: json.stargazers_count }
}
ooade commented 5 years ago

store.getState() returns an object i.e. the global states. So you could do store.getState().user, and so on... But if you don't plan on using it in getInitialProps, you can use the connect function from react-redux to mapStateToProps, and get the value through this.props.

michaelmota commented 5 years ago

Closing this issue, solved it with the mapStateToProps. Thanks @ooade !