Reputation: 363
I am learning how to use Redux with React in an effort to share state across components and I've been trying to make a navigation bar that changes colors based on which div you click. At a high level I want Redux to allow me to do the following...
<Navigation>
<NavEndpt /> <-- click an endpoint and you set this.props.active to true
<NavEndpt /> <-- resets the rest and set this.props.active to false
</Navigation>
So basically the components change color when you click them and that is really hard to do in React, I have come to find out. I have my dispatcher wired up correctly according to the tutorial I am watching and I'm still having no success changing state.
Maybe I don't totally understand Redux. I am going to provide snippets in the order best suited for understanding of my issue and maybe you can spot where I've gone wrong.
client.js
that gets attached to index.html nothing special.
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import Layout from "./components/Layout"
import store from "./store"
const app = document.getElementById("app");
ReactDOM.render(<Provider store={store}><Layout /></Provider>, app);
store.js
:
import { applyMiddleware, createStore } from "redux";
import reducer from "./reducers/mainReducer"
export default createStore(reducer)
Here is navReducer.js
that sets the initial state:
export default function reducer(state = {
about: {
id: "about",
title: "Who I am",
active: false
},
projects: {
id: "projects",
title: "What I do",
active: false
}
}, action) {
switch (action.type) {
case "CLICK": {
return {...state, active: true }
break
}
}
return state
}
Here is navActions.js
that has one action...
export function activeBox(){
return{
type: "CLICK",
}
}
Here are the components that make use of the above snippets. One note, I am not receiving any errors in the console or terminal which makes me believe that my issue is simply a bad path to a file.
Layout.js
import React from "react";
import Nav from "./views/Nav"
import { connect } from "react-redux";
@connect((store) => {
return{
about: store.navControls.about,
projects: store.navControls.projects
};
})
export default class Layout extends React.Component {
render(){
return(
<div id="wrapper">
<Nav about={this.props.about} projects={this.props.projects} dispatch={this.props.dispatch} />
<div id="content"></div>
</div>
)
}
}
Nav.js
(featured in Layout
)
import React from "react";
import NavEndpt from "./NavEndpt"
const Nav = React.createClass({
render(){
let about = this.props.about;
let projects = this.props.projects;
return(
<div id="navigation">
<NavEndpt data={about} dispatch={this.props.dispatch} />
<NavEndpt data={projects} dispatch={this.props.dispatch} />
</div>
)
}
})
export default Nav;
and finally NavEndpt.js
import React from "react";
import { activeBox } from "../../actions/navActions.js"
const NavEndpt = React.createClass({
handleClick(){
let data = this.props.data;
console.log("clicked " + data.active)
this.props.dispatch(activeBox()); // this does nothing, checking out React in the chrome dev tools shows no change to about.active or projects.active
},
styleUpdate(){
let data = this.props.data;
if (data.active === false){
return{
background: "violet"
}
} else if (data.active === true){
return {
background: "yellow"
}
}
},
render(){
let data = this.props.data;
return(
<div className="navButton" onClick={this.handleClick} style={this.styleUpdate()}>
<span>{data.title}</span>
</div>
)
}
})
export default NavEndpt;
Upvotes: 0
Views: 363
Reputation: 2691
this.props.dispatch(activeBox());
dispatches the following action to your redux store:
{
type: "CLICK",
}
That action does not identify which nav item was clicked, it just says that a click occurred. Similarly, your reducer will go from
{
about: {
id: "about",
title: "Who I am",
active: false
},
projects: {
id: "projects",
title: "What I do",
active: false
}
}
to
{
about: {
id: "about",
title: "Who I am",
active: false
},
projects: {
id: "projects",
title: "What I do",
active: false
},
active: true
}
You're just adding an active flag adjacent to about
and projects
, not in them. Check out redux's todomvc example to see how to best identify which item got clicked and have your store update appropriately. One solution is
//navActions.js
export function activeBox(nav){
return{
type: "CLICK",
nav: nav
}
}
// navReducer.js
export default function reducer(state = {
about: {
id: "about",
title: "Who I am",
active: false
},
projects: {
id: "projects",
title: "What I do",
active: false
}
}, action) {
switch (action.type) {
case "CLICK": {
// Copy state over to prevent altering previous state
let newState = {
about: ...state.about,
projects: ...state.projects
}
// Set all nav items to inactive, when you have more navItems you'll want to loop
newState.about.active = false
newState.projects.active = false
// if action.nav is a proper nav item, set it to active
if (newState[action.nav]) {
newState[action.nav].active = true
}
return newState
break
}
}
return state
}
Upvotes: 2