Creating one reducer for similar kind of actions - reactjs

In my project, I am persisting state of option buttons in redux. There are different buttons group and I am handling their click action in single function handleClick. But it seems like it’s not working. Should I create a different handler for each button group? Can anyone suggest the best solution?
code :
import React, { Component } from "react";
import { Button } from "semantic-ui-react";
import { withRouter } from "react-router";
import Answers from "../Answers/Answers";
import { handleClick } from "../../actions/handleClickAction"
import { connect } from 'react-redux'
class Section extends Component {
handleClick = event => {
this.props.handleClick(event);
};
render() {
console.log(this.state);
let styles = {
width: '50%',
margin: '0 auto',
marginBottom: '15px'
}
const { history } = this.props;
const { que1, que2, que3 } = this.state;
return (
<>
<p>1. I was stressed with my nerves on edge.</p>
<Button.Group widths="5" onClick={this.handleClick} style={styles}>
<Answers selected={this.state.que1} style={{ backgroundColor: 'red' }} />
</Button.Group>
{` `}
<p>2. I lost hope and wanted to give up when something went wrong.</p>
<Button.Group widths="5" onClick={this.handleClick} style={styles}>
<Answers selected={this.state.que2} style={{ backgroundColor: 'red' }} />
</Button.Group>
{` `}
<p>3. I feel very satisfied with the way I look and act</p>
<Button.Group widths="5" onClick={this.handleClick} style={styles}>
<Answers selected={this.state.que3} style={{ backgroundColor: 'red' }} />
</Button.Group>
<p />
{` `}
<Button
disabled={!que1 || !que2 || !que3}
onClick={() => history.push("/section2", [this.state])}
>
NEXT
</Button>
</>
);
}
}
export default withRouter(connect(null, { handleClick })(Section));
main.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import store from "./store";
import { Provider } from 'react-redux'
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById("root"));
index.js
import { combineReducers } from "redux";
import selectOptionReducer from "./selectOptionReducer";
export default combineReducers({
selectOption: selectOptionReducer
})
selectOptionReducer.js
import { SELECT_OPTION } from "../actions/types"
const initialState = {
que1: "",
que2: "",
que3: "",
que4: "",
que5: ""
}
export default (state = initialState, action) => {
switch (action.type) {
case SELECT_OPTION:
return {
...state,
que1: action.payload,
que2: action.payload,
que3: action.payload,
que4: action.payload,
que5: action.payload
};
default:
return state;
}
}
store.js
import { createStore } from 'redux'
import selectOptionReducer from "./reducers/selectOptionReducer";
const store = createStore(selectOptionReducer);
export default store;
handleClickAction.js
import { SELECT_OPTION } from "./types"
export const handleClick = e => {
return {
type: SELECT_OPTION,
payload: e.target.attributes.getNamedItem("data-key").value
}
}
output :

From what I can see, that reducer would be setting the state for all questions to the same answer on every action.
You need a way to specify which question is being answered.
I would go with something like the following which creates a custom onClick handler for each question and passes the question id to the action creator to be included in the reducer payload. The reducer then uses that id to only update the question being answered.
(untested)
selectOptionReducer.js
export default (state = initialState, action) => {
switch (action.type) {
case SELECT_OPTION:
const { questionId, value } = action.payload;
return { ...state, [questionId]: value };
default:
return state;
}
}
handleClickAction.js
export const handleClick = ({ questionId, e }) => {
return {
type: SELECT_OPTION,
payload: { questionId, value: e.target.attributes.getNamedItem("data-key").value }
}
}
component
class Section extends Component {
handleClick = questionId => e => {
this.props.handleClick({ questionId, e });
};
...
<Button.Group widths="5" onClick={this.handleClick("que1")} style={styles}>

Related

simple submit form using Redux

I am trying to create a simple form app, where there will be a textarea input and a submit button. Where, if I type something in the textarea and then click submit, the text that I just typed will show under the button inside a tag. When im doing this without Redux, it works fine, even after when I use Redux partly meaning when I manage only one state (input field state) using Redux it works great. But when i make two reducers, and two dispatches then problem happens. Here are my codes.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Provider from 'react-redux/es/components/Provider';
import {
createStore,
applyMiddleware,
combineReducers,
} from 'redux';
import { getInput, getOutput } from './reducer';
import { createLogger } from 'redux-logger';
import App from './App';
import reportWebVitals from './reportWebVitals';
const rootReducer = combineReducers({
getInput,
getOutput,
});
const logger = createLogger();
const store = createStore(
rootReducer,
applyMiddleware(logger)
);
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();
app.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {
changeInput,
postOutput,
} from './action';
import {
Form,
Button,
Container,
} from 'react-bootstrap';
const mapStateToProps = (state) => {
return {
input: state.getInput.input,
output: state.getOutput.output,
};
};
const mapDispatchToProps = (dispatch) => {
return {
handleInput: (event) =>
dispatch(changeInput(event.target.value)),
handleClick: (props) =>
dispatch(postOutput(props.output)),
};
};
class App extends Component {
// constructor() {
// super();
// this.state = {
// output: '',
// };
// }
// handleInput = (event) => {
// this.setState({ input: event.target.value });
// };
// handleClick = () => {
// this.setState({
// output: this.props.input,
// });
// };
render() {
return (
<div>
<Container>
{' '}
<Form>
<Form.Group controlId='exampleForm.ControlTextarea1'>
<div>
<div
style={{
display: 'flex',
justifyContent: 'center',
marginTop: '20px',
marginBottom: '10px',
}}>
<Form.Control
as='textarea'
rows={5}
placeholder='enter something here'
onChange={this.props.handleInput}
style={{ width: '500px' }}
/>
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
}}>
<Button
variant='primary'
onClick={this.props.handleClick}>
Submit
</Button>
</div>
</div>
</Form.Group>
</Form>
</Container>
<div
style={{
display: 'flex',
justifyContent: 'center',
}}>
<h1 value={this.props.input}>
{this.props.output}
</h1>
</div>
</div>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
action.js
import {
CHANGE_INPUT_FIELD,
POST_OUTPUT,
} from './constant';
export const changeInput = (text) => ({
type: CHANGE_INPUT_FIELD,
payload: text,
});
export const postOutput = (text) => ({
type: POST_OUTPUT,
payload: text,
});
reducer.js
import {
CHANGE_INPUT_FIELD,
POST_OUTPUT,
} from './constant';
const initialStateInput = {
input: '',
};
const initialStateOutput = {
output: '',
};
export const getInput = (
state = initialStateInput,
action = {}
) => {
switch (action.type) {
case CHANGE_INPUT_FIELD:
return Object.assign({}, state, {
input: action.payload,
});
default:
return state;
}
};
export const getOutput = (
state = initialStateOutput,
action = {}
) => {
switch (action.type) {
case POST_OUTPUT:
return Object.assign({}, state, {
output: action.payload,
});
default:
return state;
}
};
constant.js
export const CHANGE_INPUT_FIELD =
'CHANGE_INPUT_FIELD';
export const POST_OUTPUT = 'POST_OUTPUT';
changeInput action must be handled inside the component there is no reason to dispatch an action and handle it with reducer because reducer is for managing shared states.
Can you specify what is the "problem"?
The problem is not with actions, you cannot see the value because the value is set to undefined
In App.js you have to pass the correct value
onClick={this.props.handleClick}> must change as onClick={this.props.handleClick(this.props)}> otherwise props will be equal to event object in the line handleClick: (props) => dispatch(postOutput(props.output))
Still you won't see the value in UI because the output value is set to '' because you are not setting the input value to the output value in reducer.
My suggestion there must be another action that fires when submit button is clicked and sets the current input value to the input, then fire getOutput

How to pass a Redux action into 2 reducers to update state in React?

My app has a clickable item in CurrentStatus component that passes a service.id to the parent component Dashboard and gets service_notes via a Redux action with axios.get. service_notes are passed into a reducer and into the Redux store. I then connect to the store in ServiceLogs component and iterate through the array to display in render() on the DOM. ServiceLogs is a comments type component where a user can add notes. I'm able to create the notes but can't update the state. My latest approach was taking the CREATE_NOTE action and using it in notesReducer AND serviceNotesReducer. This still doesn't update state and the DOM.
Here is my layout:
Here are the relevant components:
Dashboard:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Container, Grid, Button } from "semantic-ui-react";
import CurrentStatus from "./components/CurrentStatusComponent";
import KnownOutages from "./components/KnownOutagesComponent";
import ServiceLogs from "./components/ServiceLogsComponent";
import { getServices } from "./actions/getServicesAction";
import { getServiceNotes } from "./actions/getServiceNotesAction";
import { getOutages } from "./actions/getOutagesAction";
class Dashboard extends Component {
state = {
serviceNotes: null,
serviceOutages: null,
showServiceLogs: "none",
}
componentDidMount() {
this.props.getServices();
this.props.getOutages();
}
displayServiceLogs = serviceId => {
debugger
this.props.getServiceNotes(serviceId)
this.setState({ showServiceLogs: "none" ? "block" : "none"});
}
render() {
console.log(this.state)
return (
<>
<Container fluid>
<h1>TML Dashboard</h1>
</Container>
<Grid columns={3} divided>
<Grid.Row>
<Grid.Column width={5}>Service Log</Grid.Column>
<Grid.Column width={6}>Current Status</Grid.Column>
<Grid.Column width={3}>Known Outages</Grid.Column>
</Grid.Row>
<Grid.Row>
<Grid.Column width={5}>
<ServiceLogs showServiceLogs={this.state.showServiceLogs}/>
</Grid.Column>
<Grid.Column width={6}>
<CurrentStatus displayServiceLogs={this.displayServiceLogs}/>
</Grid.Column>
<Grid.Column width={3}>
<KnownOutages />
</Grid.Column>
</Grid.Row>
</Grid>
</>
);
}
}
const mapStateToProps = state => {
return {
services: state.services.services,
notes: state.notes.notes
}
}
const mapDispatchToProps = dispatch => {
return {
getServices: () => dispatch(getServices()),
getNotes: () => dispatch(getNotes()),
getOutages: () => dispatch(getOutages()),
getServiceNotes: serviceId => dispatch(getServiceNotes(serviceId))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
Here is the CurrentStatus component where I click on an item (service) and pass the id up to Dashboard to get from in getServiceNotes(serviceId) function in getServiceNotesActoin:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Table, Header } from "semantic-ui-react";
const uuidv4 = require("uuid/v4")
class CurrentStatus extends Component {
handleClick = serviceId => {
this.props.displayServiceLogs(serviceId)
}
render() {
console.log(Object.keys(this.props.services))
return (
<>
<Table celled padded>
<Table.Header>
<Table.Row>
<Table.HeaderCell singleLine>Service</Table.HeaderCell>
<Table.HeaderCell>Status</Table.HeaderCell>
<Table.HeaderCell>Reason</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{Object.assign(this.props.services).map((service) => (
<Table.Row key={uuidv4()}>
<Table.Cell
onClick={() => this.handleClick(service.id)}
>
<Header as="h3" textAlign="center">
{service.name}
</Header>
</Table.Cell>
<Table.Cell textAlign="center">
{service.is_down ? (
<h4 style={{ color: "red" }}>Down</h4>
) : (
<h4 style={{ color: "green" }}>Up</h4>
)}
</Table.Cell>
<Table.Cell></Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</>
);
}
};
const mapStateToProps = state => {
return {
services: state.services.services
}
};
export default connect(mapStateToProps, null)(CurrentStatus);
and here is the ServiceLogs component where I'm able to display and show the related serviceNotes:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Comment, Container, Grid, Form, Button } from "semantic-ui-react";
import { createNote } from "../actions/createNoteAction";
class ServiceLogsComponent extends Component {
state = {
entry: ""
}
handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleSubmit = e => {
e.preventDefault()
const userId = 2
const serviceId = this.props.serviceNotes[0].service.id
this.props.createNote(this.state.entry, serviceId, userId)
}
render() {
console.log(this.props)
return (
<>
<div style={{ display: this.props.showServiceLogs }}>
<Comment>
<Comment.Group>
{this.props.serviceNotes.map((serviceNote) => (
<Comment.Content>
<Comment.Author as="a">{serviceNote.created_at}</Comment.Author>
<Comment.Metadata>{serviceNote.user.username}</Comment.Metadata>
<Comment.Text>{serviceNote.entry}</Comment.Text>
</Comment.Content>
))}
<Form onSubmit={(e) => this.handleSubmit(e)}>
<Form.TextArea
style={{ height: "50px" }}
onChange={this.handleChange}
name="entry"
/>
<Form.Button
type="submit"
content="Add Note"
labelPosition="left"
icon="edit"
primary
/>
</Form>
</Comment.Group>
</Comment>
</div>
</>
);
}
}
const mapStateToProps = state => {
return {
services: state.services.services,
notes: state.notes.notes,
serviceNotes: state.serviceNotes.serviceNotes
};
};
const mapDispatchToProps = dispatch => {
return {
createNote: (entry, serviceId, userId) => dispatch(createNote(entry, serviceId, userId))
}
};
export default connect(mapStateToProps, mapDispatchToProps)(ServiceLogsComponent);
So I cannot updated the DOM when I create a new note. I tried this in these 2 reducers:
const initialState = {
notes: [],
};
export const notesReducer = (state = initialState, action) => {
switch (action.type) {
case "GET_NOTES":
return { ...state, notes: action.payload };
case "CREATE_NOTE":
return {
...state,
notes: [...state.notes, action.payload],
};
default:
return state;
}
};
and
const initialState = {
serviceNotes: [],
};
export const serviceNotesReducer = (state = initialState, action) => {
switch (action.type) {
case "GET_SERVICE_NOTES":
return { ...state, serviceNotes: action.payload };
case "CREATE_SERVICE":
return { ...state, serviceNotes: [ ...state.serviceNotes, action.payload] }
default:
return state;
}
};
Hope this is clear enough. In short: I need ServiceLogs state to change on CRUD action.
Maybe you should follow the recommendations of the Redux Style Guide: https://redux.js.org/style-guide/style-guide
I guess the idea is to keep just one store for the entire app and you should wrap the connection to the store in a higher level (componently speaking) using the component.
I would also recommend for you to use the new Redux Hooks like useSelector and convert your class components to use React Hooks to simplify your code.
https://reactjs.org/docs/hooks-intro.html

When I delete an employee in the app, It is saying "ID IS NOT DEFINED". My front end is React and Redux. Backend is Node,Express and Mongo DB

I am having an issue that When I try to delete the employee, It says ID is not defined.
Frontend was built with react redux. Backend was built with Node,Express and Mongo DB.
I would appreciate if could help me to fix the issue please
//ACTION CREATORS..
import axios from "axios";
import {
GET_EMPLOYEES,
ADD_EMPLOYEE,
UPDATE_EMPLOYEE,
DELETE_EMPLOYEE
} from "./types";
export const getEmployees = () => async dispatch => {
await axios.get("/api/items").then(res =>
dispatch({
type: "GET_EMPLOYEES",
payload: res.data
})
);
};
export const deleteEmployee = id => async dispatch => {
await axios.delete(`/api/items/${id}`).then(res =>
dispatch({
type: "DELETE_EMPLOYEE",
payload: id
})
);
};
//REDUCERS...
import {
GET_EMPLOYEES,
ADD_EMPLOYEE,
UPDATE_EMPLOYEE,
DELETE_EMPLOYEE
} from "../actions/types";
const initialState = {
employees: []
};
const employeeReducer = (state = initialState, action) => {
switch (action.type) {
case "GET_EMPLOYEES":
return {
...state,
employees: action.payload
};
case "DELETE_EMPLOYEE":
return {
...state,
employees: state.employees.filter(emp => emp._id !== action.payload)
};
default:
return state;
}
};
export default employeeReducer;
//MY COMPONENT
import React, { Component } from "react";
import {
Container,
Table,
Button,
Card,
CardText,
CardBody,
CardTitle,
CardSubtitle
} from "reactstrap";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import { getEmployees, deleteEmployee } from "../actions";
import uniqid from "uniqid";
import { connect } from "react-redux";
class EmployeeList extends Component {
componentDidMount() {
this.props.getEmployees();
}
renderList() {
return this.props.employee.employees.map(emp => {
return (
<div key={emp._id}>
<Card style={{ marginBottom: "0.5rem" }}>
<CardBody>
<CardTitle>Employee Name : {emp.employee}</CardTitle>
<CardSubtitle style={{ marginBottom: ".5rem" }}>
Position : {emp.position}
</CardSubtitle>
<CardSubtitle style={{ marginBottom: ".5rem" }}>
Salary : {emp.salary}
</CardSubtitle>
<Button
color="danger"
onClick={this.onDeleteClick.bind(this, id)}
>
Delete
</Button>
</CardBody>
</Card>
</div>
);
});
}
onDeleteClick(id) {
this.props.deleteEmployee(id);
}
render() {
console.log(this.props);
return (
<Container>
<Button color="dark" style={{ marginBottom: "2rem" }}>
Add Employee
</Button>
<div>{this.renderList()}</div>
</Container>
);
}
}
const mapStateToProps = state => {
return { employee: state.employees };
};
export default connect(mapStateToProps, { getEmployees, deleteEmployee })(
EmployeeList
);
<Button
color="danger"
onClick={this.onDeleteClick.bind(this, id)}
>
I guess there is no "id" in current scope, try using "emp._id"

How would i see updated props without refreshing

How to update the state of the props when a user likes a post?
The props would need to automatically update when a user clicks like.
Currently, a user can like a post, and only on page refresh I am able to see the updated number of likes, which shows on
{this.props.likeCount}
What Component lifecycle would be best for seeing the updated props without refreshing the page? this application is using redux.
Like.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faCoffee, faAdjust } from '#fortawesome/free-solid-svg-icons';
import {connect} from 'react-redux';
import { getLikeCount} from '../actions/';
class Like extends Component{
constructor(props){
super(props);
this.state = {
likes: null
}
}
getLikes = (id) => {
// console.log(id);
this.props.getLikeCount(id)
console.log(this.props.likeCount)
}
render(){
return(
<div style={{float:'right', fontSize: '1.5em', color:'tomato'}} >
<i style={{ marginRight: '140px'}} className="fa fa-heart-o">
<span style={{ marginLeft: '6px'}}>
<a href="#" onClick={this.props.like}>Like </a>
{this.getLikes(this.props.postId)}
</span>
{/* gets the like counts */}
{this.props.likeCount}
</i>
</div>
)
}
}
const mapStateToProps = (state) => ({
isEditingId: state.post.isEditingId,
likeCount:state.post.likes
})
const mapDispatchToProps = (dispatch) => ({
// pass creds which can be called anything, but I just call it credentials but it should be called something more
// specific.
getLikeCount: (id) => dispatch(getLikeCount(id)),
// Pass id to the DeletePost functions.
});
export default connect(mapStateToProps, mapDispatchToProps)(Like);
Actions.js
export const getLikeCount = (id) => {
return (dispatch, getState) => {
return Axios.get(`/api/posts/likes/count/${id}`)
.then( (res) => {
const data = res.data
console.log(data);
dispatch({type: GET_LIKES_COUNT, data})
})
}
}
Reducer
import { GET_LIKES_COUNT} from '../actions/';
const initialState = {
post: [],
postError: null,
posts:[],
isEditing:false,
isEditingId:null,
likes:[],
postId:null
}
export default (state = initialState, action) => {
switch (action.type) {
case GET_LIKES_COUNT:
// console.log(action.data)
return({
...state,
likes:action.data
})
default:
return state
}
}
edit(im getting a wierd infinite post loop)
wierd error
Update the code to the following code.
GET_LIKES_COUNT handles the api action, of getting the number of likes for a post.
Without it, it will be always set to 0 likes on render.
ADD_LIKE action gives it the functionality of updating the state without refreshing the page.(i know that their is more specific term they call this in react, maybe its re-rendering) Update the state without re-rendering the component as well as the most important part which is making the api call to the backend to allow the user to like a post. We set likes to 0 to make it possible to upvote the state and it to have it updated without refresh.
Thanks for the assistance #novonimo.
Reducer
import { GET_LIKES_COUNT, ADD_LIKE} from '../actions/';
const initialState = {
post: [],
postError: null,
posts:[],
isEditing:false,
isEditingId:null,
likes:0,
postId:null
}
export default (state = initialState, action) => {
switch (action.type) {
// get number of likes from api
case GET_LIKES_COUNT:
// console.log(action.data)
return({
...state,
likes:action.data
})
case ADD_LIKE:
return({
...state,
likes: state.likes + 1
})
default:
return state
}
}
Actions
export const postLike = (id) => {
return (dispatch) => {
// console.log(userId);
return Axios.post('/api/posts/like', {
postId: id
}).then( (like) => {
dispatch({type: ADD_LIKE})
// console.log('you have liked this', like)
}).catch( (err)=> {
console.log('there seem to be an error', err);
})
}
}
export const getLikeCount = (id) => {
return (dispatch, getState) => {
return Axios.get(`/api/posts/likes/count/${id}`)
.then( (res) => {
const data = res.data
console.log(data);
dispatch({type: GET_LIKES_COUNT, data})
})
}
}
PostItem.js
import React, { Component } from 'react';
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import moment from 'moment';
import Editable from './Editable';
import {connect} from 'react-redux';
import {UpdatePost, getLikeCount, postLike} from '../actions/';
import Like from './Like';
import Axios from '../Axios';
const Styles = {
myPaper: {
margin: '20px 0px',
padding: '20px'
},
button:{
marginRight:'30px'
}
}
class PostItem extends Component{
constructor(props){
super(props);
this.state = {
disabled: false,
}
}
onUpdate = (id, title) => () => {
// we need the id so expres knows what post to update, and the title being that only editing the title.
if(this.props.myTitle !== null){
const creds = {
id, title
}
this.props.UpdatePost(creds);
}
}
clickLike = (id) => () => {
this.props.postLike(id);
}
render(){
const {title, id, userId, removePost, createdAt, post_content, username, editForm, isEditing, editChange, myTitle, postUpdate, likes} = this.props
return(
<div>
<Typography variant="h6" component="h3">
{/* if else teneray operator */}
{isEditing ? (
<Editable editField={myTitle ? myTitle : title} editChange={editChange}/>
): (
<div>
{title}
</div>
)}
</Typography>
<Typography component="p">
{post_content}
<h5>
by: {username}</h5>
<Typography color="textSecondary">{moment(createdAt).calendar()}</Typography>
<Like like={this.clickLike(id)} postId={id}/>
</Typography>
{!isEditing ? (
<Button variant="outlined" type="submit" onClick={editForm(id)}>
Edit
</Button>
):(
// pass id, and myTitle which as we remember myTitle is the new value when updating the title
<div>
<Button
disabled={myTitle.length <= 3}
variant="outlined"
onClick={this.onUpdate(id, myTitle)}>
Update
</Button>
<Button
variant="outlined"
style={{marginLeft: '0.7%'}}
onClick={editForm(null)}>
Close
</Button>
</div>
)}
{!isEditing && (
<Button
style={{marginLeft: '0.7%'}}
variant="outlined"
color="primary"
type="submit"
onClick={removePost(id)}>
Remove
</Button>
)}
</div>
)
}
}
const mapStateToProps = (state) => ({
isEditingId: state.post.isEditingId,
})
const mapDispatchToProps = (dispatch) => ({
// pass creds which can be called anything, but i just call it credentials but it should be called something more
// specific.
UpdatePost: (creds) => dispatch(UpdatePost(creds)),
getLikeCount: (id) => dispatch(getLikeCount(id)),
postLike: (id) => dispatch( postLike(id))
// Pass id to the DeletePost functions.
});
export default connect(null, mapDispatchToProps)(PostItem);
Like.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faCoffee, faAdjust } from '#fortawesome/free-solid-svg-icons';
import {connect} from 'react-redux';
import { getLikeCount} from '../actions/';
class Like extends Component{
constructor(props){
super(props);
this.state = {
likes: null
}
}
getLikes = (id) => {
// console.log(id);
this.props.getLikeCount(id)
console.log(this.props.likeCount)
}
render(){
return(
<div style={{float:'right', fontSize: '1.5em', color:'tomato'}} >
<i style={{ marginRight: '140px'}} className="fa fa-heart-o">
<span style={{ marginLeft: '6px'}}>
<a href="#" onClick={this.props.like}>Like </a>
{this.getLikes(this.props.postId)}
</span>
{/* gets the like counts */}
{this.props.likeCount}
</i>
</div>
)
}
}
const mapStateToProps = (state) => ({
isEditingId: state.post.isEditingId,
likeCount:state.post.likes
})
const mapDispatchToProps = (dispatch) => ({
getLikeCount: (id) => dispatch(getLikeCount(id)),
// Pass id to the DeletePost functions.
});
export default connect(mapStateToProps, mapDispatchToProps)(Like);
React philosophy is based on remove Refresh pages on changes.
so forget refresh in all react app.
in the component you can change code like this:
handleAddUpVote = ()=> this.props.dispatch(addUpVote())
return(
<div onClick={this.handleAddUpVote}> sth </div>
)
and in action:
const ADD_UP_VOTE = "ADD_UP_VOTE";
const addUpVote = ({type: ADD_UP_VOTE});
export {ADD_UP_VOTE, addUpVote}
and finally, change your reducer:
initialState={
voteCounter: 0
}
const Reducer = (state=initialState, action) => {
switch(action.type){
case(ADD_UP_VOTE):
return{
...state,
voteCounter: state.voteCounter + 1
};
}
}

Redux: Props undefined

Redux: Undefined Prop
Undefined Prop
I want to pass down a prop from my container component, down to my presentational component via props, but I haven't been able to do so without the prop being undefined. Why isn't the number prop being passed down to the presentational component?
Creating the store with the initial stae and rootreducer:
import {createStore, applyMiddleware} from 'redux';
...
import rootReducer from './reducers/rootReducer';
const initialState = {
multiply: 2,
number: 1
}
export const store = createStore(
...initialState,
rootReducer,
applyMiddleware(logger(), thunk),
window.devToolsExtension && window.devToolsExtension()
);
Reducer for multiply actions and divide actions:
const multiplyReducer = (state = {}, action) => {
switch (action.type) {
case 'MULTIPLY':
return state = {
...state,
number: state.number * action.payload
}
case 'DIVIDE':
return state = {
...state,
number: state.number / action.payload
}
default:
return state;
}
}
export default multiplyReducer;
Root reducer:
import {combineReducers} from 'redux';
import multiplyReducer from './multiplyReducer';
const rootReducer = combineReducers({
multiply: multiplyReducer
});
export default rootReducer;
Wrapping the app in a Provider:
import Multiplier from './ContainerComponents/Multiplier';
import { BrowserRouter, Route } from 'react-router-dom';
const App = ({ store }) => (
<BrowserRouter>
<Route path="/" exact component={Multiplier}/>
</BrowserRouter>
);
export default App;
Actions:
export const multiplyAction = {
type: 'MULTIPLY',
payload: 2
}
export const divideAction = {
type: 'DIVIDE',
payload: 2
}
Container Component:
import MultiplierDisplay from '../StyleComponents/MultiplierDisplay';
import {connect} from 'react-redux';
import {multiplyAction, divideAction} from '../Redux/actions/multiplyActions';
class Multiplier extends React.Component {
render() {
return (<MultiplierDisplay {...this.props} />)
}
};
const mapStateToProps = (state) => {
return {multiply: state.multiply, number: state.number}
};
const mapDispatchToProps = (dispatch) => {
return {
handleClick: (event) => {
dispatch(multiplyAction)
},
handleClick2: (event) => {
dispatch(divideAction)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Multiplier);
Presentational Component:
const MultiplierDisplay = (props) => {
return (
<div
className="top"
style={{
alignContent: 'center',
justifyContent: 'center'
}}>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo"/>
<h1 className="App-title">Welcome to React</h1>
</header>
</div>
<h1 style={{
marginLeft: 20
}}>
Multiply the count:
</h1>
<p style={{
fontSize: '3em'
}}>
Count: {props.number}
</p>
<button
style={{
marginLeft: 100,
width: '20%',
height: '20%'
}}
onClick={props.handleClick}
title="Multiply">
Multiply
</button>
<button
style={{
marginLeft: 50,
width: '20%',
height: '20%'
}}
onClick={props.handleClick2}
title="Divide">
Divide
</button>
</div>
)
}
export default MultiplierDisplay;
Your prop number isn't passed on to the presentation component being you haven't passed it down from the container,
class Multiplier extends React.Component {
render() {
return (<MultiplierDisplay/>) // this is where you are not passing it down
}
};
If you need to pass all the props down to the MultiplierDisplay component you can write
class Multiplier extends React.Component {
render() {
return (<MultiplierDisplay {...this.props}/>)
}
};
or if you want to pass selected props down, you can write them like
class Multiplier extends React.Component {
render() {
const { multiply, number, handleClick, handleClick2 } = this.props;
return (<MultiplierDisplay multiply={multiply} number={number} handleClick={handleClick} handleClick2={handleClick2}/>)
}
};
Also you need to get the state from state.multiple you are having number and multiply in multiplierReducer which you are using as multiply
const mapStateToProps = (state) => {
return {multiply: state.multiply.multiply, number: state.multiply.number}
};
const mapDispatchToProps = (dispatch) => {
return {
handleClick: (event) => {
dispatch(multiplyAction)
},
handleClick2: (event) => {
dispatch(divideAction)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Multiplier);
You can use this below to your code
const mapStateToProps = (state) => {
return {multiply: state.multiply.multiply, number: state.multiply.number}
};
There is a problem in how you are mapping you state to props of your Multiplier component as written in combineReducer you have added multiply as the state name for your multiplyReducer, hence in your mapStateToProps function you need to can access your multiplyReducer state by the following code:
const mapStateToProps = (state) => {
return {multiply: state.multiply.multiply, number: state.multiply.number}
};
Thanks I hope this helps.

Resources