React state up 2 player components reset button - javascript

I am new on React.js and I don't understand,
how I should pass the score children to app parent,
and reset all components score via "Reset Components Score" on the app parent button,
I try everything noting work,
thanks for the help!
Player children components:
import React, { useState } from 'react';
function Player(props) {
const [score, setScore] = useState(0);
function scoreUp(params) {
setScore(score + 1);
}
function scoreDown(params) {
score <= 0 ? setScore(0) : setScore(score - 1);
}
const scoreReset = () => {
setScore(0);
};
return (
<div>
<center>
<h1>{props.name}</h1>
<h2>{score}</h2>
<button style={{ backgroundColor: 'green' }} onClick={scoreUp}>
+
</button>
<button style={{ backgroundColor: 'red' }} onClick={scoreDown}>
-
</button>
<button style={{ backgroundColor: 'purple' }} onClick={scoreReset}>
Reset {props.name} Score
</button>
</center>
</div>
);
}
export default Player;
App Parent:
import React from 'react';
import Player from './Player';
function App() {
return (
<div>
<Player name='Benjamin' />
<Player name='Alex' />
<button style={{ backgroundColor: 'black' }}>
Reset Components Score
</button>
</div>
);
}
export default App;
the App img

Benja. In react, data flows only in one direction (from parent to child). In this case, we want to lift the state one level up ( the parent component: App) so that both the parents and children have access to the state. so we will take the score state from the player component to the app component. then we will pass the score and a handler function as well as props to the player component.
with that our code will look like this =>
the App component:
import React, { useState } from "react";
import Player from "./components/Player";
interface PlayerProps {
[key: string]: number;
}
function App() {
const [playerScore, resetPlayerScore] = useState<PlayerProps>({
Benjamin: 0,
Alex: 0,
});
const handle_playerScore = (player: string) => {
const scoreUp = () => {
resetPlayerScore({ ...playerScore, [player]: playerScore[player] + 1 });
};
const scoreDown = () => {
resetPlayerScore({ ...playerScore, [player]: playerScore[player] - 1 });
};
const scoreReset = () => {
resetPlayerScore({ ...playerScore, [player]: 0 });
};
return { scoreUp, scoreDown, scoreReset };
};
const resetAll = () => {
resetPlayerScore({ Benjamin: 0, Alex: 0 });
};
return (
<div>
<Player
score={playerScore.Benjamin}
name="Benjamin"
handleScore={handle_playerScore}
/>
<Player
score={playerScore.Alex}
name="Alex"
handleScore={handle_playerScore}
/>
<button onClick={resetAll}>Reset All</button>
</div>
);
}
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
and our Player component:
import React from "react";
interface PlayerProps {
name: string;
score: number;
handleScore: (player: string) => {
scoreUp: () => void;
scoreDown: () => void;
scoreReset: () => void;
};
}
const Player = ({ name, score, handleScore }: PlayerProps) => {
const { scoreUp, scoreDown, scoreReset } = handleScore(name);
return (
<div>
<h1>{name}</h1>
<h2>{score}</h2>
<button style={{ backgroundColor: "green" }} onClick={scoreUp}>
+
</button>
<button style={{ backgroundColor: "red" }} onClick={scoreDown}>
-
</button>
<button style={{ backgroundColor: "purple" }} onClick={scoreReset}>
Reset {name} Score
</button>
</div>
);
};
export default Player;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
We can apply other techniques as well to solve this scenario. such as Higher-order components and render props. They also have the same underlying principle.

Related

It is necessary that when the component is removed, it does not just disappear, but leaves behind component with button

thank you in advance, however, before answering the question, read carefully what I ask for help with all due respect. What i need:
I need that when the delete button is clicked, the component is not only deleted, but also leaves behind another button, by clicking on which, the remote component is rendered back
Functionality that already works: rendering a component on click, as well as deleting by a button
import React, {useState} from 'react';
import ReactDOM from 'react-dom';
interface IParams {
id: number;
}
interface IBlock {
deleteBlock(blockToDelete: number) : void
id: number
}
function App() {
const [newBlock, setNewBlock] = useState([] as IParams[])
const createOnClick = () => {
const newId = {
id: newBlock.length + 1
}
setNewBlock([...newBlock, newId])
}
const deleteBlock = (blockToDelete: number) => {
setNewBlock(
newBlock.filter((x) => {
return x.id !== blockToDelete
})
)
}
const FunctionalBlock: React.FC<IBlock> = ({id, deleteBlock}) => {
return (
<div style={{display: 'flex', maxWidth: '300px', justifyContent: 'space-between'}}>
<div>i was created {id} times</div>
<button onClick={() => {
deleteBlock(id)
}}>now delete me</button>
</div>
)
}
return (
<div className="App">
<button onClick={createOnClick}>
New block
</button>
{
newBlock.map((x) => (
<FunctionalBlock id={x.id} key={x.id} deleteBlock={deleteBlock}/>
))
}
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
Why not just set a state on the block?
const {useState} = React
const FunctionalBlock = ({ id, idx, isDeleted, toggleBlockState }) => {
return (
<div
style={{
display: "flex",
maxWidth: "300px",
justifyContent: "space-between"
}}
>
{
!isDeleted
? <React.Fragment>
<div>i was created {idx} times</div>
<button
onClick={toggleBlockState}
>
now delete me
</button>
</React.Fragment>
: <button onClick={toggleBlockState}>REVIVE BLOCK</button>
}
</div>
)
;
};
const getNewBlock = (idx) => ({
id: Date.now(),
idx,
isDeleted: false,
})
const toggleIsDeletedById = (id, block) => {
if (id !== block.id) return block
return {
...block,
isDeleted: !block.isDeleted
}
}
const App = () => {
const [newBlock, setNewBlock] = useState([])
const createOnClick = () => {
const block = getNewBlock(newBlock.length + 1)
setNewBlock([...newBlock, block])
}
const toggleBlockStateById = (id) => {
setNewBlock(newBlock.map((block) => toggleIsDeletedById(id, block)))
}
return (
<div>
<div>NEW BLOCK</div>
<div><button onClick={createOnClick}>ADD NEW BLOCK +</button></div>
<div>
{
newBlock.map(block => <FunctionalBlock {...block} toggleBlockState={() => toggleBlockStateById(block.id)}/>)
}
</div>
</div>
);
};
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
<script crossorigin src="https://unpkg.com/react#18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js"></script>
<div id="root"></div>

update the state of a component from another component

I want to update icon of component from another component. whenever I clicked on a playlist music start playing and icon should be change to pause instead of play but I don't know how I can update the state of a component from another component.
PlayList Component -
playlist and musics are in this component
class PlayList extends React.Component {
render() {
const playMusic = () => {
musics.forEach(e => e.pause());
musics[this.props.arr].play();
musics[this.props.arr].currentTime = 0;
nowPlaying = this.props.arr;
clickedOnMusic = 'clicked';
};
return (
<div>
<Card>
<CardActionArea onClick={playMusic} />
</Card>
</div>
)
}
BottomAppBar Component -
icons and some function to playing music are here
class BottomAppBar extends React.Component {
state = {
displayPlay: 'none',
displayPause: '',
displayVolume: '',
displayMute: 'none'
};
render(){
return(
<IconButton onClick={handleChangePlay} style={{ color: 'white' }}>
<PauseCircleOutlineRoundedIcon
style={{ fontSize: 46, display: this.state.displayPlay }}
/>
<PlayCircleOutlineIcon
style={{ fontSize: 46, display: this.state.displayPause }}
/>
)
}
thank you very much for reading !
Wrap them in a container and maintain their states over there.
Ex:
<Parent>
<PlayList/>
<BottomAppBar />
</Parent>
You can use the context api, any ascendent of PlayerLogic can access whatever you put in the context with React.useContext and will be re rendered when values in the Context change.
const PlayerContext = React.createContext();
const PlayerLogic = ({ children }) => {
const [state, setState] = React.useState({
playing: false,
});
const setPlaying = React.useCallback(
val =>
setState(current => ({ ...current, playing: val })),
[]
);
const pause = React.useCallback(() => setPlaying(false), [
setPlaying,
]);
const play = React.useCallback(() => setPlaying(true), [
setPlaying,
]);
return (
<PlayerContext.Provider
value={{
state,
pause,
play,
}}
>
{children}
</PlayerContext.Provider>
);
};
const ComponentOne = () => {
const {
pause,
play,
state: { playing },
} = React.useContext(PlayerContext);
return (
<div>
{playing ? (
<button onClick={pause}>pause</button>
) : (
<button onClick={play}>play</button>
)}
</div>
);
};
class ComponentTwo extends React.Component {
render() {
return this.context.state.playing
? 'now playing'
: 'nothig is playing';
}
}
ComponentTwo.contextType = PlayerContext;
const A = () => <B />;
const B = () => <C />;
const C = () => {
const {
state: { playing },
} = React.useContext(PlayerContext);
return `In component C, is playing ${JSON.stringify(
playing
)}`;
};
const App = () => (
<PlayerLogic>
<ComponentOne />
<ComponentTwo />
<div>
<A />
</div>
</PlayerLogic>
);
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Could not add multiple counters in react

I want to make a counter app with increment, decrement and add counter button. But add counter function is not working. It's showing this error:
Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...?
i have already tried enclosing it in tags,still its not working.
import React,{Component} from 'react';
import './App.css';
export class App extends Component {
state={
count:0,
}
increment=()=>{
this.setState({ count: this.state.count + 1 });
}
decrement=()=>{
this.setState({ count: this.state.count - 1 });
}
addCounter=()=>{
<span><button onClick={this.increment}>+</button></span>
<span>{this.state.count}</span>
<span><button onClick={this.decrement}>-</button></span>
}
render() {
return (
<div className="App">
<button onClick={this.addCounter}>Add Counter</button>
</div>
)
}
}
export default App;
add counter function should add another counter just below the previous counter.
Basically extract a Counter component.
Then have your App maintain a list of Counter components.
// Counter Component
export class Counter extends React.Component {
state = {
count:0,
}
increment=()=>{
this.setState({ count: this.state.count + 1 });
}
decrement=()=>{
this.setState({ count: this.state.count - 1 });
}
render() {
return (
<div>
<span><button onClick={this.increment}>+</button></span>
<span>{this.state.count}</span>
<span><button onClick={this.decrement}>-</button></span>
</div>
);
}
}
// App Component
export class App extends React.Component {
state = {
counters: [], // additional state for Counter components
}
addCounter = () => {
this.setState({
counters: [
...this.state.counters,
Counter
]
})
}
render() {
return (
<div className="App">
<button onClick={this.addCounter}>Add Counter</button>
{ this.state.counters.map((Counter, index) => (
<Counter key={index} />)
)}
</div>
)
}
}
Demo
You are just adding more spans and button but refering to the same counter.
state={
i : 0,
count:0,
}
var newCount="counter"+this.state.i;
this.setState({
i : this.state.i+1,
})
this.setState({
count: {
...this.state.count,
newCount: 0
}
});
So with this you add a new counter with a progresive autoincrement number.
I think it is what you want to do.
const { useState } = React;
function App(){
const [counter, setCounter] = useState([]);
function addCounter(){
setCounter(counter.concat({id: counter.length, count: 0}));
}
function increase(id){
setCounter(counter.map(el=>{
if(el.id === id){
el.count++;
}
return el;
}));
}
function decrease(id){
setCounter(counter.map(el=>{
if(el.id === id){
el.count--;
}
return el;
}));
}
return (
<div className="App">
<button onClick={addCounter}>Add Counter</button>
{
counter.map(el=>{
return(
<div key={el.id}>
<span>Counter #{el.id}</span>
<div>
<button onClick={()=>{increase(el.id)}}>+</button>
<span>{el.count}</span>
<button onClick={()=>{decrease(el.id)}}>-</button>
</div>
</div>
)
})
}
</div>
)
}
ReactDOM.render(
<App />, document.getElementById('root')
)
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Your "counter" needs to be a react component with its own state, what you have there will have each "counter" you add use the same component state from App. You also do not save the returned JSX to then render anywhere.
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div> // <-- React components can return only a single node*
<span>
<button onClick={() => setCount(count + 1)}>+</button>
</span>
<span>{count}</span>
<span>
<button onClick={() => setCount(count - 1)}>-</button>
</span>
</div>
);
};
class App extends Component {
state = {
counters: []
};
addCounter = () => {
this.setState(prevState => ({
counters: [...prevState.counters, <Counter />]
}));
};
render() {
return (
<div>
<button onClick={this.addCounter}>Add Counter</button>
{this.state.counters}
</div>
);
}
}
* React render lifecycle function can render arrays.
You have to add another functional Component instead of adding JSX. DEMO
import React from 'react';
const counter = (props) => {
return (
<div>
<span><button onClick={() => props.increment(props.index)}>+</button></span>
<span>{props.count}</span>
<span><button onClick={() => props.decrement(props.index)}>-</button></span>
</div>
)
}
export default counter;
And your main App component
import React, {Component} from 'react';
import Counter from './Counter';
import './App.css';
export class App extends Component {
state={
counters: []
}
valueChanger = (index, inc) => {
this.setState((prevState) => {
const counters = prevState.counters.slice();
counters[index] += inc;
return {
counters: counters
}
});
}
increment=(index)=>{
this.valueChanger(index, 1);
}
decrement=(index)=>{
this.valueChanger(index, -1);
}
addCounter=()=>{
this.setState((prevState) => {
return { counters: [...prevState.counters, 0] }
});
}
render() {
let counterElems = this.state.counters.map((c, index) => {
return <Counter key={index} index={index} increment={this.increment} decrement={this.decrement} count={c} />
});
return (
<div className="App">
{counterElems}
<button onClick={this.addCounter}>Add Counter</button>
</div>
)
}
}
export default App;

How do I access a function that is being updated from a redux store properly?

I have this component that I split for easy management. Before splitting everything worked as expected, after splitting I am getting an error when I click on an icon which calls the createReactionsIcon. Error says
TypeError: updateReaction is not a function
onClick
./components/home/components/SingleUpload.jsx:26
23 |
24 | return icons.map(({ key, text, type }) => (
25 | <IconText
> 26 | onClick={() => updateReaction(item.id, key)}
| ^ 27 | key={key}
28 | type={type}
29 | text={text}
How can I access this correctly from my Home component where updateReaction is returning updateReaction from the redux store.
SubComponent
import PropTypes from 'prop-types';
import React from 'react';
import { Avatar, Card, Icon, List } from 'antd';
import { LIST_TEXTS, STYLES } from '../constants';
const { AVATAR, CARD_CONTAINER, CARD_LIST, ICON, USER_LIST } = STYLES;
const { INNER, MORE, UPLOAD, VERTICAL } = LIST_TEXTS;
const IconText = ({ type, text, onClick }) => (
<span>
<Icon type={type} style={ICON} onClick={onClick} />
{text}
</span>
);
function createReactionsIcon(item, updateReaction) {
const { like, dislike, maybe } = item.reactions;
const icons = [
{ key: 'like', text: `${like.count}`, type: 'heart' },
{ key: 'dislike', text: `${dislike.count}`, type: 'dislike' },
{ key: 'maybe', text: `${maybe.count}`, type: 'meh' },
];
return icons.map(({ key, text, type }) => (
<IconText
onClick={() => updateReaction(item.id, key)}
key={key}
type={type}
text={text}
/>
));
}
export default class SingleUpload extends React.Component {
render() {
const { values } = this.props;
return (
<div style={CARD_CONTAINER}>
<List
itemLayout={VERTICAL}
dataSource={values}
renderItem={item => {
const { avatar, description, id, uploader: { image, name } } = item;
return (
<List.Item style={USER_LIST}>
<Card
actions={createReactionsIcon(item, this.updateReaction)}
cover={<img alt={UPLOAD} src={image} />}
extra={<Icon type={MORE} />}
hoverable
key={id}
title={(
<a href="/">
<Avatar src={avatar} style={AVATAR} />
{name}
</a>
)}
type={INNER}
style={CARD_LIST}
>
{description}
</Card>
</List.Item>
);
}}
/>
</div>
);
}
}
Home.js
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import SingleUpload from './SingleUpload';
import ComparisonUpload from './ComparisonUpload';
import { STYLES } from '../constants';
import * as actions from '../actions';
import { getUploads } from '../selectors';
const { CARD_CONTAINER } = STYLES;
class Home extends React.Component {
componentDidMount() {
const { actions: { requestUploadList } } = this.props;
requestUploadList();
}
updateReaction = (id, reaction) => {
const { actions: { updateReaction } } = this.props;
const payload = { id, reaction };
updateReaction(payload);
}
render() {
const { uploads } = this.props;
return (
<div style={CARD_CONTAINER}>
<SingleUpload values={[...uploads.values()]} />
<ComparisonUpload values={[...uploads.values()]} />
</div>
);
}
}
Home.propTypes = {
actions: PropTypes.objectOf(PropTypes.object),
uploads: PropTypes.instanceOf(Map),
};
const mapStateToProps = state => ({
uploads: getUploads(state),
});
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(actions, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Pass your function to component as props,
<SingleUpload values={[...uploads.values()]} updateReaction = {this.updateReaction}/>
Now you can use this in your child component,
<IconText onClick={() => this.props.updateReaction(item.id, key)}
You can pass the updateReaction from your parent to child as a callback
<SingleUpload values={[...uploads.values()]} hanldeReaction={this.updateReaction} />
And you can access it in the child using props.hanldeReaction
<Card actions={createReactionsIcon(item, this.props.hanldeReaction)}
You have to pass down the updateReaction() event-handler you defined in Home as a prop to SingleUpload. Then you can access that prop from anywhere inside your component.
Which means we can cleanup the actions prop inside the Card since we only need to pass the item now.
<Card actions={createReactionsIcon(item)}
As well as createReactionsIcon, now we just call that prop directly inside the function
function createReactionsIcon(item) {
const { like, dislike, maybe } = item.reactions;
const icons = [
{ key: 'like', text: `${like.count}`, type: 'heart' },
{ key: 'dislike', text: `${dislike.count}`, type: 'dislike' },
{ key: 'maybe', text: `${maybe.count}`, type: 'meh' },
];
return icons.map(({ key, text, type }) => (
<IconText
onClick={() => this.props.updateReaction(item.id, key)}
key={key}
type={type}
text={text}
/>
));
}
Less redundant code overall which sounds like what you are trying to achieve.

onClick function fires after clicking twice

For some reason, the onClick will not fire once, but if clicked twice. The this.props.PostLike(id) action will be fired, What should i do to make it work only on one click.
Also, the heart state works fine, in just one click. Its just the this.props.postLike needs to be clicked twice for it work.
And using e.preventDefault() does not solve the issue.
....
clickLike = (id) => {
this.props.postLike(id);
// toggles between css class
this.setState({
heart: !this.state.heart
})
}
render(){
return(
<div style={{float:'right', fontSize: '1.5em', color:'tomato'}} >
<i style={{ marginRight: '140px'}} className={this.state.heart ? 'fa fa-heart':'fa fa-heart-o' }>
<span style={{ marginLeft: '6px'}}>
<a href="#" onClick={() =>this.clickLike(this.props.like)}>Like</a>
</span>
{/* gets the like counts */}
<span style={{ marginLeft: '7px'}} >{this.props.likes} </span>
</i>
</div>
)
}
}
const mapStateToProps = (state) => ({
})
const mapDispatchToProps = (dispatch) => ({
postLike: (id) => dispatch( postLike(id))
// Pass id to the DeletePost functions.
});
export default connect(null, mapDispatchToProps)(Like);
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, postLike, getCount} 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,
myId: 0,
likes:0
}
}
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);
}
}
render(){
const {title, id, userId, removePost, createdAt, post_content, username, editForm, isEditing, editChange, myTitle, postUpdate, Likes, clickLike, myLikes} = this.props
return(
......
{/* likes get like counts */}
<Like like={id} likes={myLikes} />
.......
Posts.js
render() {
const {loading} = this.state;
const {myPosts} = this.props
console.log(this.state.posts);
if (!this.props.isAuthenticated) {
return (<Redirect to='/signIn'/>);
}
if (loading) {
return "loading..."
}
return (
<div className="App" style={Styles.wrapper}>
<h1>Posts</h1>
{/* <PostList posts={this.state.posts}/> */}
<div>
{this.state.posts.map(post => (
<Paper key={post.id} style={Styles.myPaper}>
<PostItem myLikes={post.Likes.length} // right here
myTitle={this.state.title} editChange={this.onChange} editForm={this.formEditing} isEditing={this.props.isEditingId === post.id} removePost={this.removePost} {...post}/>
</Paper>
))}
</div>
</div>
);
}
}
PostList.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 {connect} from 'react-redux';
import { withRouter, Redirect} from 'react-router-dom';
import {DeletePost, postLike, UpdatePost,EditChange, GetPosts, getCount, DisableButton} from '../actions/';
import PostItem from './PostItem';
import _ from 'lodash';
const Styles = {
myPaper: {
margin: '20px 0px',
padding: '20px'
}
}
class PostList extends Component{
constructor(props){
super(props);
this.state ={
title: '',
posts:[],
loading:true
}
}
componentWillMount() {
this.props.GetPosts();
}
componentWillReceiveProps(nextProps, prevState) {
let hasNewLike = true;
if (prevState.posts && prevState.posts.length) {
for (let index = 0; index < nextProps.myPosts.length; index++) {
if (nextProps.myPosts[index].Likes.length !== prevState.posts[index].Likes.length) {
hasNewLike = true;
}
}
}
if (hasNewLike) {
this.setState({posts: nextProps.myPosts, loading: false}); // here we are updating the posts state if redux state has updated value of likes
}
}
// Return a new function. Otherwise the DeletePost action will be dispatch each
// time the Component rerenders.
removePost = (id) => () => {
this.props.DeletePost(id);
}
onChange = (e) => {
e.preventDefault();
this.setState({
title: e.target.value
})
}
formEditing = (id) => ()=> {;
this.props.EditChange(id);
}
render(){
const { posts, loading} = this.state;
// console.log(this.props.posts)
// console.log(this.props.ourLikes);
if(loading){
return "loading..."
}
return (
<div>
{this.state.posts.map(post => (
<Paper key={post.id} style={Styles.myPaper}>
<PostItem
myLikes={post.Likes.length} // right here
myTitle={this.state.title}
editChange={this.onChange}
editForm={this.formEditing}
isEditing={this.props.isEditingId === post.id}
removePost={this.removePost}
{...post}
/>
</Paper>
))}
</div>
);
}
}
const mapStateToProps = (state) => ({
isEditingId: state.post.isEditingId,
myPosts: state.post.posts,
})
const mapDispatchToProps = (dispatch) => ({
// pass creds which can be called anything, but i just call it credentials but it should be called something more
// specific.
GetPosts: () => dispatch(GetPosts()),
EditChange: (id) => dispatch(EditChange(id)),
UpdatePost: (creds) => dispatch(UpdatePost(creds)),
postLike: (id) => dispatch( postLike(id)),
// Pass id to the DeletePost functions.
DeletePost: (id) => dispatch(DeletePost(id))
});
// without withRouter componentWillReceiveProps will not work like its supposed too.
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(PostList));
Try doing like that:
clickLike = (id) => () => {
this.props.postLike(id);
// toggles between css class
this.setState({
heart: !this.state.heart
})
}
<a href="#" onClick={this.clickLike(this.props.like)}>Like</a>
This likely has little to do with the logic you have set up and more about the html markup itself. If you were to take in the event and console.log it, you'll probably find that you are actually not hitting the tag on the first click, but one of its outer elements. Try wrapping the tag and everything else inside of it in a tag then putting the onClick logic in the button instead and you'll yield better results.
<div style={{float:'right', fontSize: '1.5em', color:'tomato'}} >
<button onClick={() =>this.clickLike(this.props.like)}>
<i style={{ marginRight: '140px'}} className={this.state.heart ? 'fa fa-heart':'fa fa-heart-o' }>
<span style={{ marginLeft: '6px'}}>
Like
</span>
{/* gets the like counts */}
<span style={{ marginLeft: '7px'}} >{this.props.likes} </span>
</i>
</button>
div>
You have to be careful when using arrow functions with the this keyword as it does not refer to the current object, but its parent object instead.
clickLike = async(id) => {
await this.props.postLike(id);
// toggles between css class
this.setState({
heart: !this.state.heart
})
}
try using the async await
<span style={{ marginLeft: '6px'}} onClick=
{()=>this.clickLike(this.props.like)}>
<a href="#" >Like</a>
</span>

Categories