Una vez tienes perfectamente conformada la arquitectura de Redux solo queda hacer las adaptaciones necesarias para tener acceso a elementos como el store y a las actions en nuestra aplicación de React y así unir ambas arquitecturas.

En el artítulo anterior de esta temática estuvimos creando los Reducers en Redux que nos permitían, como ya explicamos, filtrar cada una de las acciones y cambiar el estado atendiendo a la acción despachada.

Adaptando al store para trabajar con NextJS: next-redux-wrapper

Para poder cargar el store de Redux, deberemos de utilizar un wrapper llamado next-redux-wrapper. Aplicaremos este sobre los componentes principales de las páginas, que por si no lo recuerdas, se encuentran localizados en la carpeta pages/.

Una vez tengamos el concepto claro, procederemos a la instalación del wrapper para su posterior inclusión en la página principal(pages/index):

Debes de tener en cuenta que aunque solo vayamos a aplicarlo a la página principal como ejemplo, tú debes de hacerlo en cada una de las páginas que tengan peticiones que querramos cambiar por acciones de Redux o que tengan componentes que hagan esto.

$ npm install next-redux-wrapper

Una vez instalado, procederemos a su importación en la página principal. También deberemos de exportar el componente encapsulándolo en este wrapper. Este tipo de encapsulamientos es propio tanto de este wrapper como de la librería react-redux que veremos en próximos pasos. Por tanto, nuestro código quedaría de la siguiente manera:

import React from 'react'
import Head from 'next/head'
import withRedux from 'next-redux-wrapper'

import Posts from '../components/Posts'
import { configureStore } from '../store'

function FirstPage() {
    return(
        <div className="App">
            <Head>
                <link rel="stylesheet" href="/static/app.css"/>
                <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
            </Head>
            <Posts />
        </div>
    )
}

export default withRedux(configureStore, null, null)(FirstPage)

Si te fijas, como te estaba comentado, el poder de withRedux está en la exportación del componente, y para reforzar el concepto, debes saber que la llamada al wrapper tiene la siguiente estructura:

export default withRedux(funcionStore, mapStateToProps, mapDispatchToProps)(componente)

Como habrás apreciado, en el lugar de mapStateToProps y mapDispatchToProps no estamos pasando nada pues en nuestro caso en este archivo no tenemos ninguna petición que vayamos a sustituir por una acción de Redux. Si recuerdas, todas las peticiones se hacen desde los componentes pero es obligatorio hacer esta mínima llamada por la página, para que el componente herede el store.

Accediendo al store en los componentes que tienen que hacer peticiones con react-redux

Configurando react-redux para el componente Posts

Una vez hayamos configurado NextJS para trabajar con Redux en nuestra página principal, procederemos a configurar también este en el componente hijo, en este caso Posts.
Para ello deberemos de hacer algo similar a lo anterior, aunque esta vez con el componente react-redux y teniendo en cuenta que necesitaremos esta vez sí, la configuración de mapStateToProps y mapDispatchToProps.

$ npm install react-redux

Una vez instalado, aplicamos lo mencionado al componente:

import React from 'react'
import moment from 'moment'
import { connect } from 'react-redux'

import Header from './Header'

class Posts extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            posts: []
        }

        this.deletePost = this.deletePost.bind(this)

    }
    deletePost(ev) {
        let el = ev.target
        let id = el.dataset.id
        let index = el.dataset.index

        fetch(`https://owcrud-api.now.sh/api/posts/${id}`, {
            method: 'DELETE'
        })
         .catch(err => console.error(err))
         .then(() => {
            let posts = this.state.posts
            posts.splice(index, 1)
            this.setState({ posts })
         })
    }
    componentDidMount() {
        fetch('https://owcrud-api.now.sh/api/posts')
            .catch(err => console.error(err))
            .then(res => res.json())
            .then(posts => this.setState({ posts }))
    }
    render() {
        if(this.state.posts.length > 0) {
            return(
                <div className="App">
                    <Header />
                    <div className="Posts">
                        {
                            this.state.posts.reverse().map((post, index) => (
                                <div className="Posts-Item" key={index}>
                                    <div className="PhotoSegment">
                                        <img src={post.image} alt={post.title}/>
                                    </div>
                                    <div className="DetailsSegment">
                                        <a href={`/post/${post._id}`} className="Post-Link">
                                            <h2 className="Post-Title">{post.title}</h2>
                                        </a>
                                        <h3 className="Post-Date">{moment(post.releaseDate).fromNow()}</h3>
                                        <span
                                            className="fa fa-trash"
                                            onClick={this.deletePost}
                                            data-id={post._id}
                                            data-index={index}
                                        />
                                    </div>
                                </div>
                            ))
                        }
                    </div>
                </div>   
            )
        } else {
            return(
                <h3>Cargando...</h3>
            )
        }
    }
}

const mapStateToProps = {}

const mapDispatchToProps = {}

export default connect(mapStateToProps, mapDispatchToProps)(Posts)

Añadiendo mapStateToProps

mapStateToProps, al igual que mapDispatchToProps pueden ser definidos como unos transpiladores entre React y Redux y son los que harán que podamos tocar el state desde React.

En el caso particular de mapStateToProps no será mas que una función, que siempre retornará un objeto en el que, partiendo del parámetro state de la función colocaremos las partes del estado de Redux a las que querramos tener acceso y estas, estarán disponibles desde this.props:

const mapStateToProps = state => {
    return {
        posts: state.posts
    }
}

Sólo he procedido a añadir a posts ya que currentPosts no será necesario en este componente. Si recuerdas, esta estructura fue definida en el reducer y más concretamente en la variable INTIAL_STATE

Añadiendo mapDispatchToProps

En el caso de mapDispachToProps será otra función la cual colocará en las props funciones que, al llamarlas ejecutarán las diferentes acciones de Redux. Para ello traeremos como parámetro dispatcher que es el encargado de despachar las funciones.

Otro elemento importante será el importar las funciones desde el archivo en el que las definimos.

import { getPosts, deletePost } from '../redux/actions/index'

[...]

const mapDispatchToProps = dispatch => {
    return {
        getPosts: () => {
            dispatch(getPosts())
        },
        deletePost: (id, index) => {
            dispatch(deletePost(id, index))
        }
    }
}

Llamando a las acciones necesarias dentro de los componentes

Una vez tenemos acceso mediante las props de ReactJS a las funciones que necesitamos y al estado de Redux vamos a proceder a cambiar las peticiones que hay en los componentes por las llamadas a las acciones de Redux.

En el caso particular del componente Posts, nos encontramos que en componentDidMount estamos cargando todos los posts, que habría que sustituirlo por la función getPosts y en el método deletePost la acción del mismo nombre.

async componentDidMount() {
    await this.props.getPosts()
}
deletePost(ev) {
    let el = ev.target
    let id = el.dataset.id
    let index = el.dataset.index
    
    this.props.deletePost(id, index)
}

Adaptando los componentes para trabajar con el estado de Redux

Una vez hemos sustituido las peticiones tal cual las trabajamos en el CRUD por las acciones de Redux vamos a cambiar algunos detalles de nuestro componente.

Por ejemplo, el state dejaremos de tenerlo y con este habrá que modificar elementos como el if del método render().
Los cambios resultarían en la clase que constituye el componente dejándolo de la siguiente manera:

class Posts extends React.Component {
    constructor(props) {
        super(props)

        this.deletePost = this.deletePost.bind(this)
    }
    deletePost(ev) {
        let el = ev.target
        let id = el.dataset.id
        let index = el.dataset.index

         this.props.deletePost(id, index)
    }
    async componentDidMount() {
        await this.props.getPosts()
    }
    render() {
        if(!this.props.posts.loading) {
            return(
                <div className="App">
                    <Header />
                    <div className="Posts">
                        {
                            this.props.posts.data.reverse().map((post, index) => (
                                <div className="Posts-Item" key={index}>
                                    <div className="PhotoSegment">
                                        <img src={post.image} alt={post.title}/>
                                    </div>
                                    <div className="DetailsSegment">
                                        <a href={`/post/${post._id}`} className="Post-Link">
                                            <h2 className="Post-Title">{post.title}</h2>
                                        </a>
                                        <h3 className="Post-Date">{moment(post.releaseDate).fromNow()}</h3>
                                        <span
                                            className="fa fa-trash"
                                            onClick={this.deletePost}
                                            data-id={post._id}
                                            data-index={index}
                                        />
                                    </div>
                                </div>
                            ))
                        }
                    </div>
                </div>   
            )
        } else {
            return(
                <h3>Cargando...</h3>
            )
        }
    }
}

Conclusiones

Hemos conseguido conectar todas las implementaciones que hemos realizado en cuanto a Redux y las hemos aplicado a nuestras vistas en ReactJS.

Por tu parte quedaría, aplicar los pasos que hemos aprendido hoy para este y el resto de los componentes que lo necesitan.

En el siguiente artículo, concluiremos la guía de Redux y veremos aspectos interesantes del mismo como la diferenciación de los pasos que hemos visto el día de hoy si usáramos react-router en vez de Next.JS y el código de los cambios que hemos estado realizando entre otras aportaciones que complementarán tu aprendizaje sobre Redux.