Hseleiro Hseleiro - 1 month ago 6
React JSX Question

React-Redux - Create an Action to hide a container

I have 2 buttons, one to display the details of my Work and one to Hide it. The button that shows the details is working , but now i need to hide the detail container when i click the hide details button.

I hope im explaining myself well , please ask me if you need more information.

this is my miristica.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { selectTrabalho } from '../../actions/index';
import { hideDetails } from '../../actions/hide_details';
import TrabalhoMiristica from './detalhe_miristica';

class Miristica extends Component {

renderList(){

return this.props.trabalho_m.map((trabalho) => {
return (
<li>
<div className="row list-group">
<div className="col-md-4">
<img src={trabalho.img} />
</div>
<div className="col-md-8">
<h3>{trabalho.title}</h3>
<p>{trabalho.descricao}</p>
<button key={trabalho.id} onClick={() => this.props.selectTrabalho(trabalho)} type="button" className="show-detail btn btn-info">Detalhes</button>
<br/>
<br/>
<button onClick={() => this.props.hideDetails(trabalho)} type="button" className="btn btn-info">Esconder Detalhes</button>
<br/>
<br/>
<TrabalhoMiristica/>
</div>
</div>
</li>
);
})
}

render() {

return (

<ul className="list-group col-ms-4">
{this.renderList()}
</ul>

);

}
}


function mapStateToProps(state) {

return {
trabalho_m: state.trabalho_m
};
}

function mapDispatchToProps(dispatch) {

return bindActionCreators({ selectTrabalho: selectTrabalho, hideDetails: hideDetails }, dispatch)
}


export default connect(mapStateToProps, mapDispatchToProps)(Miristica);


This is my hide_detail.js Actiom

export function hideDetails(id, trabalho) {

return {
type: 'HIDE_DETAILS',
id,
trabalho
};

}


This is my reducer_hide_detail.js

export default function(state = {hideDetails: true}, action) {
switch(action.type) {
case 'HIDE_DETAILS':
console.log("remove detail");
}

return state;
}


This is my detail view

import React, { Component } from 'react';
import { connect } from 'react-redux';

class TrabalhoMiristica extends Component {
render() {
if (!this.props.trabalho) {
return null;
}

return (
<div>
<div>{this.props.trabalho.tec}</div>
</div>
);
}
}

function mapStateToProps(state){
return {
trabalho: state.activeMiristica
};
}


export default connect(mapStateToProps)(TrabalhoMiristica);


I have a simple console log, and its working when i click the button.
So i only need to make some action to make the button to work, but im not getting :( . Could some one help me ? Thanks in advanced.

UPDATE :

this is where trabalho_m is coming, from my reducer that have an array

import { combineReducers } from 'redux';
- > import TrabalhoMiristica from './miristica/reducer_miristica';
import ActiveMiristica from './miristica/reducer_detalhe_miristica';
- > HIDE DETAIL REDUCER import HideDetails from './miristica/reducer_hide_detail';
import TrabalhoVet from './vet/reducer_vet';
import ActiveVet from './vet/reducer_detalhe_vet';

const rootReducer = combineReducers({

- > trabalho_m: TrabalhoMiristica,
trabalho_vet: TrabalhoVet,
activeMiristica: ActiveMiristica,
- > hideDetails: HideDetails,
activeVet : ActiveVet,


});

export default rootReducer;


Reducer Miristica

export default function() {

return [

{ id: 2, title: 'Miristica', tec: "Wordpress-Woocommerce", descricao: "Ustedes pueden aceptar mi negocio o aceptar las consequências. ¿Plata o plomo? Compremos una lavadora más grande, pues. Los políticos se espantan fácil. Una pistola y ya. La cárcel em Estados Unidos es peor que la muerte. El castillo, la torre yo soy. La espada que guarda el caudal. Tu el aire que respiro yo. Y la luz de la luna en el mar. La garganta que ansio mojar. Que temo ahogar de amor. Y cuales deseos me vas a dar? Dices tu: mi tesoro basta con mirarlo. Y tuyo será, y tuyo será", img: 'http://feiraalternativa.pt/wp-content/uploads/2016/04/Miristica-Bio-Cosm%C3%A9tica.png'},

];

}


Thank you very much for the help !!

UDPDATE -

Reducers index.js

import { combineReducers } from 'redux';
import TrabalhoMiristica from './miristica/reducer_miristica';
import ActiveMiristica from './miristica/reducer_detalhe_miristica';
import HideDetails from './miristica/reducer_hide_detail';
import TrabalhoVet from './vet/reducer_vet';
import ActiveVet from './vet/reducer_detalhe_vet';

const rootReducer = combineReducers({

trabalho_m: TrabalhoMiristica,
trabalho_vet: TrabalhoVet,
activeMiristica: ActiveMiristica,
hideDetails_m: HideDetails,
activeVet : ActiveVet,


});

export default rootReducer;


i have changed the name of the reducer to hideDetails_m and i have update my stateToProps function

function mapStateToProps(state) {

return {
trabalho_m: state.trabalho_m,
hideDetails_m: state.hideDetails_m
};
}


Now im not understanding where do you get

{ this.props. - > detailsHidden <- ? null : (
<TrabalhoMiristica/>
)}


Thank You

UPDATE -

Miristica.js



return this.props.trabalho_m.map((trabalho) => {
return (
<li>
<div className="row list-group">
<div className="col-md-4">
<img src={trabalho.img} />
</div>
<div className="col-md-8">
<h3>{trabalho.title}</h3>
<p>{trabalho.descricao}</p>
<button key={trabalho.id} onClick={() => this.props.selectTrabalho(trabalho)} type="button" className="show-detail btn btn-info">Detalhes</button>
<br/>
<br/>
<button onClick={() => this.props.hideDetails(trabalho)} type="button" className="btn btn-info">Esconder Detalhes</button>
<br/>
<br/>
{ this.props.detailsHidden ? null : (
<TrabalhoMiristica/>
)}
</div>
</div>
</li>
);
})
}

function mapStateToProps(state) {

return {
trabalho_m: state.trabalho_m,
hideDetails_m: state.detailsHidden
};
}


REDUCER reducer_hide_details.js

export default function(state = {hideDetails: true}, action) {
switch(action.type) {
case 'HIDE_DETAILS':
return Object.assign({}, state, {
hideDetails: true
});
default:
return state;
}
}


Index.js Reducers

import { combineReducers } from 'redux';
import TrabalhoMiristica from './miristica/reducer_miristica';
import ActiveMiristica from './miristica/reducer_detalhe_miristica';
import HideDetails from './miristica/reducer_hide_detail';
import TrabalhoVet from './vet/reducer_vet';
import ActiveVet from './vet/reducer_detalhe_vet';

const rootReducer = combineReducers({

trabalho_m: TrabalhoMiristica,
trabalho_vet: TrabalhoVet,
activeMiristica: ActiveMiristica,
hideDetails_m: HideDetails,
activeVet : ActiveVet,


});

export default rootReducer;


Do i have to pass my state to my map function ? please help. thanks

UPDATE

Like this ?

{ this.props.hideDetails_m.hideDetails ? null : (
<TrabalhoMiristica/>
)}


function mapStateToProps(state) {

return {
trabalho_m: state.trabalho_m,
hideDetails_m: state.hideDetails_m.hideDetails
};
}


UPDATE SyntaxError: Unexpected token (11:8)

reducer_miristica.js

const initialState = [
{ showDetails: true, id: 2, title: 'Miristica', tec: "Wordpress- Woocommerce", descricao: "Ustedes pueden aceptar mi negocio o aceptar las consequências. ¿Plata o plomo? Compremos una lavadora más grande, pues. Los políticos se espantan fácil. Una pistola y ya. La cárcel em Estados Unidos es peor que la muerte. El castillo, la torre yo soy. La espada que guarda el caudal. Tu el aire que respiro yo. Y la luz de la luna en el mar. La garganta que ansio mojar. Que temo ahogar de amor. Y cuales deseos me vas a dar? Dices tu: mi tesoro basta con mirarlo. Y tuyo será, y tuyo será", img: 'http://feiraalternativa.pt/wp-content/uploads/2016/04/Miristica-Bio-Cosm%C3%A9tica.png'},
]

export default function(state = initialState, action) {
switch(action.type) {
case 'HIDE_DETAILS':
return state.map(function(item){
if (item.id == action.id) {
return Object.assign({}, state, { showDetails: false }
}
return item;
});
default:
return state;
}
}


I have put it on babel validator

repl: Unexpected token (11:8)
9 | if (item.id == action.id) {
10 | return Object.assign({}, state, { showDetails: false }
11 | }
| ^


Thank You !

UPDATE

detail_miristica.js

import React, { Component } from 'react';
import { connect } from 'react-redux';

class TrabalhoMiristica extends Component {
render() {
if (!this.props.trabalho) {
return null;
}

return (
<div>
<div>{this.props.trabalho.tec}</div>
</div>
);
}
}

function mapStateToProps(state){
return {
trabalho: state.activeMiristica
};
}


export default connect(mapStateToProps)(TrabalhoMiristica);


In the array i have tec : 'Wordpress-woocomerce' and i show that in the detail.

Thank You !!

Answer

Well first of all you have to wrap your detail view in an if-statement that refers to state.hideDetails. (I couldn't find your detail view in your question)

But I think the main part of your question is what to do in that reducer. In your reducer you have to return the new state. So in case of your 'HIDE_DETAILS action you would want to return:

return { hideDetails: true }

This would quickly break down though in case you want to add more fields to your state object. Because of that I would recommend to use Object.assign. So your reducer would look like this:

export default function(state = {hideDetails: true}, action) {
  switch(action.type) {
    case 'HIDE_DETAILS':
      return Object.assign({}, state, {
       hideDetails: true
      }); 
    default:
      return state;
    }
}

Edit:

Okay, based on your comments I think you still need help wrapping your detail view in a conditional.

I don't quite understand where this trabalho_m state is coming from but you will have to add the state.hideDetails or in case you are combining multiple reducers your state.detailReducer.hideDetails (assuming the reducer is named detailReducer) to your mapStateToProps function. Watch for naming conflicts because your action is named hideDetails as well.

Anyway you can't use if-else-statements in JSX therefore you should probably try something like this:

{ this.props.detailsHidden ? null : (
  <TrabalhoMiristica/>
)}

Edit 2:

Your naming differs and you didn't mapped the props as deep as I thought you would. So in your case it would be:

{ this.props.hideDetails_m.hideDetails ? null : (
  <TrabalhoMiristica/>
)}

Edit 3:

Now that I read over all the things you added again I noticed that I missed that you probably want to save the hidden state not globally but on the trabalho's.

I think the best way to do this is to drop your hiddenDetails_m all together and store that on the trabalho_m state.

So reducers/index.js would be:

import { combineReducers } from 'redux';
import TrabalhoMiristica from './miristica/reducer_miristica';
import ActiveMiristica from './miristica/reducer_detalhe_miristica';
import TrabalhoVet from './vet/reducer_vet';
import ActiveVet from './vet/reducer_detalhe_vet';

const rootReducer = combineReducers({

  trabalho_m: TrabalhoMiristica,
  trabalho_vet: TrabalhoVet,
  activeMiristica: ActiveMiristica,
  activeVet : ActiveVet,


});

export default rootReducer;

And reducers/reducer_miristice.js would be:

const initialState = [
  { showDetails: true,  id: 2, title: 'Miristica', tec: "Wordpress-Woocommerce", descricao: "Ustedes pueden aceptar mi negocio o aceptar las consequências. ¿Plata o plomo? Compremos una lavadora más grande, pues. Los políticos se espantan fácil. Una pistola y ya. La cárcel em Estados Unidos es peor que la muerte. El castillo, la torre yo soy. La espada que guarda el caudal. Tu el aire que respiro yo. Y la luz de la luna en el mar. La garganta que ansio mojar. Que temo ahogar de amor. Y cuales deseos me vas a dar? Dices tu: mi tesoro basta con mirarlo. Y tuyo será, y tuyo será", img: 'http://feiraalternativa.pt/wp-content/uploads/2016/04/Miristica-Bio-Cosm%C3%A9tica.png'},
]


export default function(state = initialState, action) {
  switch(action.type) {
    case 'HIDE_DETAILS':
      return state.map(function(item){
        if (item.id == action.id) {
          return Object.assign({}, item, { showDetails: false }
        }
        return item;
      });
    default:
      return state;
  }
}

This would mean you component would look something like this:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { selectTrabalho } from '../../actions/index';
import { hideDetails } from '../../actions/hide_details';
import TrabalhoMiristica from './detalhe_miristica';

class Miristica extends Component {

renderList(){

    return this.props.trabalho_m.map((trabalho) => {
        return (
            <li>
                <div className="row list-group">
                    <div className="col-md-4">
                        <img src={trabalho.img} />
                    </div>
                    <div className="col-md-8">
                        <h3>{trabalho.title}</h3>
                        <p>{trabalho.descricao}</p>
                        <button key={trabalho.id} onClick={() => this.props.selectTrabalho(trabalho)} type="button" className="show-detail btn btn-info">Detalhes</button>
                        <br/>
                        <br/>
                        <button onClick={() => this.props.hideDetails(trabalho)} type="button" className="btn btn-info">Esconder Detalhes</button>
                        <br/>
                        <br/>
                        { trabalho.showDetails ? (
                          <TrabalhoMiristica/>
                        ) : null }
                    </div>
                </div>
            </li>
        );
    })
    }

  render() {

  return (

    <ul className="list-group col-ms-4"> 
        {this.renderList()}
    </ul>

    );

  }
}


function mapStateToProps(state) {

return {
    trabalho_m: state.trabalho_m
};
}

function mapDispatchToProps(dispatch) {

return bindActionCreators({ selectTrabalho: selectTrabalho, hideDetails: hideDetails }, dispatch)
}


export default connect(mapStateToProps, mapDispatchToProps)(Miristica);

Edit 3.1:

I didn't add any information about the action.

Your action takes an id, and a trabalho but you only pass it a trabalho in your component. I think you don't need the trabalho right now so just pass in the id. This is how I assumed it at the reducer side. Your action would look like this:

export function hideDetails(id) {

return {
    type: 'HIDE_DETAILS',
    id,
};

And you would have to change this in the component:

<button onClick={() => this.props.hideDetails(trabalho)} type="button" className="btn btn-info">Esconder Detalhes</button>

to:

<button onClick={() => this.props.hideDetails(trabalho.id)} type="button" className="btn btn-info">Esconder Detalhes</button>

}

Edit 4:

I think we are going far out of scope of your question but your key warnings are a result of not passing a unique key to the <li>. (Read more about that here.)

So your component would now look like this.

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { selectTrabalho } from '../../actions/index';
import { hideDetails } from '../../actions/hide_details';
import TrabalhoMiristica from './detalhe_miristica';

class Miristica extends Component {

renderList(){

    return this.props.trabalho_m.map((trabalho) => {
        return (
            <li key={trabalho.id}>
                <div className="row list-group">
                    <div className="col-md-4">
                        <img src={trabalho.img} />
                    </div>
                    <div className="col-md-8">
                        <h3>{trabalho.title}</h3>
                        <p>{trabalho.descricao}</p>
                        <button key={trabalho.id} onClick={() => this.props.selectTrabalho(trabalho)} type="button" className="show-detail btn btn-info">Detalhes</button>
                        <br/>
                        <br/>
                        <button onClick={() => this.props.hideDetails(trabalho)} type="button" className="btn btn-info">Esconder Detalhes</button>
                        <br/>
                        <br/>
                        { trabalho.showDetails ? (
                          <TrabalhoMiristica/>
                        ) : null }
                    </div>
                </div>
            </li>
        );
    })
    }

  render() {

  return (

    <ul className="list-group col-ms-4"> 
        {this.renderList()}
    </ul>

    );

  }
}


function mapStateToProps(state) {

return {
    trabalho_m: state.trabalho_m
};
}

function mapDispatchToProps(dispatch) {

return bindActionCreators({ selectTrabalho: selectTrabalho, hideDetails: hideDetails }, dispatch)
}


export default connect(mapStateToProps, mapDispatchToProps)(Miristica);