Italik
Italik

Reputation: 696

How to pass props to reducer? ReactJs and Redux

I'm struggling with react-redux thing quite a lot of hours. I want to show to user <Alert /> when isVisible value is true. I'm still don't understand well architecture of redux. So, I'm trying to pass value to reducer but it doesn't work. What I'm doing wrong?

Alert component:

export default () => {
        return (
            <div className="alert alert-warning">
                <strong>Warning!</strong> Indicates a warning that might need attention.
            </div>
        );
    };

Weather container:

    renderWeather(cityData) {
        const name = cityData.city.name;
        const temps = _.map(cityData.list.map(weather => weather.main.temp), (temp) => temp - 273);

    return (
        <tr key={name}>
            <td><Chart data={temps} color="orange" units="C" /></td>
            </td>
        </tr>
    )
}
    render() {
        return (
            <div>
                {this.props.isVisible ? null : <Alert /> }
                <table className="table table-hover">
                    <thead>
                        <tr>
                            <th>City</th>
                            <th>Temperature (C)</th>
                        </tr>
                    </thead>
                    <tbody>
                        {this.props.weather.map(this.renderWeather)}
                    </tbody>
                </table>
            </div>
        )
    }
}

function mapStateToProps({ weather, isVisible }) {
    return { weather, isVisible };
}

export default connect(mapStateToProps)(WeatherList);

Index reducer:

const rootReducer = combineReducers({
  weather: WeatherReducer,
  isVisible: WeatherReducer
});

export default rootReducer;

Reducer:

export default function (state = [], action) {
    switch (action.type) {
    case FETCH_WEATHER:
        return [...state, action.payload];
    case FETCH_WEATHER_ERROR: 
        return {state, isVisible: false}
    }
    return state;
}

Action:

export function fetchWeather (city) {
    const url = `${ROOT_URL}&q=${city}`;

    return (dispatch) => {
        axios.get(url);
        .then(({data}) => {
            dispatch({type: FETCH_WEATHER, payload: data});
        })
        .catch((error) => { 
            dispatch({type: FETCH_WEATHER_ERROR, payload: error});
        });
    };
}

Upvotes: 1

Views: 5410

Answers (4)

Arkadiy  Afonin
Arkadiy Afonin

Reputation: 124

Split code into smaller reducers:

const rootReducer = combineReducers({
  weather: weatherReducer,
  isVisible: isVisibleReducer
});

export default rootReducer;


// reducers:
const weatherReducer = (state = [], action) => {
    if (action.type === FETCH_WEATHER) {
        return action.payload; // data object
    }
    if (action.type === FETCH_WEATHER_ERROR) {
        return action.payload; // error object
    }
    return state;
};

const isVisibleReducer = (state = false, action) => {
    if (action.type === FETCH_WEATHER) {
        return true;
    }
    if (action.type === FETCH_WEATHER_ERROR) {
        return false;
    }
    return state;
};

export { weatherReducer, isVisibleReducer };

Now each reducer function has own zone of control.

In your reducer you have mixed type of state. Default is an empty array [], but later you have returned an object ({state, isVisible: false}). Don't do that. You also reduce weather and isVisible nodes of state by the same WeatherReducer. It's not correct, because the logic of modifying this nodes is different.

Upvotes: 1

Nicy Anto
Nicy Anto

Reputation: 146

You have to decide if your state need to be an array or an object. From your reducer, initial state is an empty array, but if FETCH_WEATHER is dispatched it returns array and FETCH_WEATHER_ERROR returns object.

I suggest you modify combineReducers as well, there is no point passing same reducer to different properties in root state.

const rootReducer = combineReducers({
 weather: WeatherReducer,
});

is enough. Lets assume your state is an Object, with isVisible and weather properties. So your reducer will become.

export default function (state = {isVisible:false, weather:[]}, action) 
 {
  switch (action.type) {
   case FETCH_WEATHER:
     return Object.assign({}, {weather: action.payload, isVisible:false})
   case FETCH_WEATHER_ERROR: 
     return Object.assign({}, {weather:null, isVisible:true})
   }
   return state;
 }

And finally if you want to show when isVisible is true you have to

{this.props.isVisible ? <Alert /> : null }

Upvotes: 2

yurii kosygin
yurii kosygin

Reputation: 154

you need an Action that will change the isVisible eq

function changeVisible() {
 return {
   type: FETCH_WEATHER_ERROR
  }
}

and

case FETCH_WEATHER_ERROR: 
   return {...state, isVisible: true}

Upvotes: 0

Sabin Bogati
Sabin Bogati

Reputation: 741

{this.props.isVisible ? null : <Alert /> } should be replace by {this.props.isVisible ? <Alert /> : null } because when this.props.isVisible is true then only will be visible but you are doing it wrong.. Hoped it helped

Upvotes: 0

Related Questions