How to correctly bind React onClick event with Redux? - reactjs

Basically there's no sign that the event is binded somewhere and it's not firing. Here's the Component
class AgendaPointsList extends React.Component {
constructor(props) {
super(props);
this.onAgendaPointClick = this.props.onAgendaPointClick.bind(this);
}
render() {
let items = this.props.agenda_points.map((a, i) => {
return <AgendaPoint key={i} agenda_point={a} index={i} onClick={this.onAgendaPointClick} />
})
console.log(this.props)
return (
<table>
<tbody>
{items}
</tbody>
</table>
);
}
}
The console.log(this.props) outputs:
Object
item_point: Object
item_points: Array[4]
onItemPointClick: onItemPointClick(id)
onModalCloseClick: onModalCloseClick(id)
store: Object
storeSubscription: Subscription
__proto__: Object
Here's the redux component:
const OPEN_AGENDA_POINT = 'meeting/OPEN_AGENDA_POINT'
const CLOSE_AGENDA_POINT = 'meeting/CLOSE_AGENDA_POINT'
const initialState = {
modal_is_open: false,
point_id: 0,
point_data: {}
}
const openAgendaPoint = function (id) {
return {
type: OPEN_AGENDA_POINT,
id: id
}
}
const closeAgendaPoint = function (id) {
return {
type: CLOSE_AGENDA_POINT,
id: id
}
}
const agendaPointsReducer = function (state = initialState, action) {
switch (action.type) {
case OPEN_AGENDA_POINT: {
state.modal_is_open = true,
point_id = action.id
}
case CLOSE_AGENDA_POINT: {
state.modal_is_open = false
}
default:
return state
}
}
const agendaPointsUiStateProps = (state) => {
return {
agenda_point: state.point_data
}
}
const agendaPointsUiActions = (dispatch) => {
return {
onAgendaPointClick: (id) => {
console.log(id)
dispatch(openAgendaPoint(id))
},
onModalCloseClick: (id) => {
dispatch(closeAgendaPoint(id))
}
}
}
const store = Redux.createStore(
agendaPointsReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
// Usage:
const AgendaPointsList = connectWithStore(
store,
AgendaPointsList,
agendaPointsUiStateProps,
agendaPointsUiActions
)
That's the child component:
class AgendaPoint extends React.Component {
render() {
return (
<tr>
<td>{ this.props.index + 1 }</td>
<td>{ this.props.agenda_point.title}</td>
<td>6</td>
<td>{ this.props.agenda_point.agenda_time } min</td>
</tr>
);
}
}
I tried multiple ways of binding the event:
onClick={this.props.onAgendaPointClick.bind(a.id, this)
onClick={this.props.onAgendaPointClick(a.id, this).bind(this)
onClick={() => this.props.onAgendaPointClick(a.id))
Non seem to work.
Using this for reac-redux connect wrapper to pass in store. This is running on Ruby on Rails Sprockets beta4.
What is the correct way of doing this?

You want the on click to be on you tag.
With the following code change you event will be triggerd:
class AgendaPoint extends React.Component { render() {
return (
<tr onClick={this.props.onClick}>
<td>{ this.props.index + 1 }</td>
<td>{ this.props.agenda_point.title}</td>
<td>6</td>
<td>{ this.props.agenda_point.agenda_time } min</td>
</tr>
); } }

Try binding the event in your ItemList constructor:
constructor(props) {
super(props);
this.onItemClick = this.onItemClick.bind(this);
}
Then in your ItemList render function ...
let items = this.props.agenda_points.map((a, i) => {
return <Item key={i} agenda_point={a} index={i} onClick={this.props.onItemClick} />
})
This assumes that the onItemClick function is defined in ItemList parent, and is being passed in as a prop.

Related

delete three level component(a component have an array of component, each have an array of compoent)

I would appreciate it greatly if you could let me know how to deal with this problem.
I hava a page component which has an array of company component, and each company has an array of contract.
If I delete any company, every company component will re-render, so I cant put array of contract under each company state, so I put it under page component state.
The question is If I delete one company, how can I correctly re-render all contracts under each component.
Thank you for reading my problem, and sorry for my poor English:(
Error Message is "TypeError: Cannot read property 'contractList' of undefined"
My page code is...
class IRPage extends Component {
// // initialize our state
state = {
companyList: [],
};
addCompanyArr = (newCompany) => {
this.setState(
state => {
const list = state.companyList.push(newCompany);
return {
list,
};
}
)
};
addContractArr = (index, newContract) => {
this.setState(
state => {
const list = state.companyList[index].contractList.push(newContract);
return {
list,
};
}
);
}
setCompanyArr = () => {
this.setState(
state => {
const list = state.companyList;
return {
list,
};
}
)
};
render() {
return (
<div className="container m-5">
<IRContent companyList={this.state.companyList} setCompanyArr={this.setCompanyArr} addCompanyArr={this.addCompanyArr} addContractArr={this.addContractArr}></IRContent>
</div>
)
}
}
export default IRPage;
My content code is ...
class IRContent extends React.Component {
constructor(props) {
super(props);
}
addCompany = () => {
const companyNode = <IRCompany companyList={this.props.companyList} setCompanyArr={this.props.setCompanyArr} index={this.props.companyList.length} addContractArr={this.props.addContractArr}/>;
const newCompany = {
node: companyNode,
contractList: [],
};
this.props.addCompanyArr(newCompany);
}
render() {
return(
<div className="container col-sm-12 justify-content-center">
<h1 align="center">data</h1>
<hr/>
{
this.props.companyList.map((element, index) => {
return <div key={"myCompanyKey_"+index+"_"+this.props.companyList.length} id={index}>{element.node}</div>;
})
}
<button color="primary" onClick = {this.addCompany}>
add new company
</button>
</div>
);
}
}
export default IRContent;
My company code is...
class IRCompany extends React.Component {
constructor(props) {
super(props);
}
deleteCompany = event => {
event.preventDefault();
var targetID = event.target.parentElement.parentElement.parentElement.parentElement.parentElement.getAttribute("id")
this.props.companyList.splice(targetID, 1);
this.props.setCompanyArr();
};
addContract = event => {
event.preventDefault();
var newContract = <IRContract/>;
var targetID = event.target.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.getAttribute("id");
this.props.addContractArr(targetID, newContract);
this.forceUpdate();
};
render() {
return(
<div>
{
this.props.companyList[this.props.index].contractList.map((element, index) => {
return <React.Fragment key={"myContractKey" + index + "_" +this.props.companyList[this.props.index].contractList.length}>{element}</React.Fragment>;
})
}
<button color="primary" onClick = {this.addContract}>主約</button>
</div>
);
}
}
export default IRCompany;
I can successively add company and contract, but there are some problem on deleing.
Thank you for reading my problem, and sorry for my poor English:(

Reactjs not re-rendering update received from WebSockets

I am using WebSockets to update upvotes on comments in React. I am receiving comment updates in logs of different client instances. However, React does not render the updates to upvotes.
Code I am trying:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
class Comment extends Component {
constructor(props){
super(props);
this.upvotes = React.createRef();
this.downvotes = React.createRef();
this.handleUpvote = this.handleUpvote.bind(this);
this.handleDownvote = this.handleDownvote.bind(this);
}
handleUpvote(){
console.log(this.props);
const json = { type: 'upvote' };
json.data = this.props;
json.data.comment.upvotes++;
console.log(json);
this.props.socket.send(JSON.stringify(json));
}
handleDownvote(){
this.downvotes.current.innerHTML++;
console.log(this.downvotes.current.innerHTML);
}
render() {
return (
<tr>
<td>{this.props.comment.user.firstName} {this.props.comment.user.lastName}</td>
<td>{this.props.comment.content }</td>
<td> <span ref={this.upvotes}>{this.props.comment.upvotes}</span> <button onClick={this.handleUpvote}>Upvote</button> </td>
<td> <span ref={this.downvotes}>{this.props.comment.downvotes}</span> <button onClick={this.handleDownvote}>Downvote</button> </td>
</tr>
)
}
}
export default class ListComments extends Component {
constructor(props){
super(props);
this.state = { comments: [] }
}
componentDidMount(){
axios.get('http://localhost:5000/api/comments/')
.then(resp => this.setState({ comments : resp.data }))
.catch(err => console.log(err));
}
componentWillReceiveProps(nextProps){
const data = JSON.parse(nextProps.comment);
console.log(data.data);
if(data.type === "upvote"){
// const a = this.state.comments;
// a.forEach(comment => {
// if(comment._id == data.data.comment._id){
// comment = data.data.comment
// }
// });
// this.setState({ comments : a })
this.setState(prevState => {
// Get previous state
const { comments } = prevState;
// Add new item to array
comments.forEach(comm => {
if(comm._id == data.data.comment._id){
comm = data.data.comment
}
});
// Return new state
return { comments };
});
}
else if(data.type === "comment"){
this.setState({ comments : [data.data, ...this.state.comments] })
}
}
commentList() {
return this.state.comments.map(currentcomment => {
return <Comment comment={currentcomment} socket={this.props.actions} key={currentcomment._id}/>;
})
}
render() {
return (
<div>
<h3>Comments</h3>
<table className="table">
<thead className="thead-light">
<tr>
<th>Username</th>
<th>Content</th>
<th>Upvotes</th>
<th>Downvotes</th>
</tr>
</thead>
<tbody>
{ this.commentList() }
</tbody>
</table>
</div>
);
}
}
Outputs I am getting -
Client one with 3 upvotes to question 1
Client 2 with updates to upvotes received in console, not rendred in actual comment

React-Redux. Redux state changes but data not being refreshed on page

I am new at Redux and quite incompetent with React in general. Sorry if this is a stupid question.
I have a simple table with data. I clicked on header and data at state sorted but this changes dont displayed on page. Data displayed only one time and not re-renders.
body
let OurBody = ({ filteredArr }) => {
return (
<tbody>
{filteredArr.map(element =>
<tr key={element._id}>
<td>{element.company}</td>
<td>{element.balance}</td>
<td>{element.registered}</td>
<td>{element.address.country}</td>
<td>{element.employers.length}</td>
<td>--</td>
</tr>
)}
</tbody>
)
}
const mapStateToProps = (state) => {
return {
filteredArr: getSortedCompanies(state.json, state.sortCompany)
};
}
export default connect(mapStateToProps)(OurBody);
header
let Headersort = ({ dispatch }) => (
<thead>
<tr>
<td onClick={() => {dispatch(sortCompany());}}>Company</td>
<td>Balance</td>
<td>Registered</td>
<td>Country</td>
<td>Number of employers</td>
<td>Show employers</td>
</tr>
</thead>
);
const mapStateToProps = (state) => ({
Arr: state.json,
})
export default connect(mapStateToProps)(Headersort);
App component
class App extends React.Component {
componentDidMount() {
this.props.dispatch(fetchPosts());
}
render() {
const { loading, error } = this.props;
if (error) {
return <div>Error! {error.message}</div>;
}
if (loading) {
return <div>Loading...</div>;
}
return (
<div>
<table>
<Headersort />
<OurBody />
</table>
</div>
)
}
}
const mapStateToProps = state => ({
loading: state.loading,
error: state.error
})
App = connect(mapStateToProps)(App)
export default App;
Reducer works correctly. Every time I clicked data at store changes.
No errors displayed.
Code at github https://github.com/DrGreenNow/React-Redux-Table
Since you are using sort to sort the data, it sorts the data at original reference and when it updates, the data returned by mapStateToProps has the same reference. Now when connect accepts this data from mapStateToProps, it doesn't trigger a re-render because according to it the data hasn't changed.
Making a clone of data while sorting works.
//getSortedCompanies
export default (json, sortCompany) => {
if (sortCompany === null) {
console.log(sortCompany);
return json;
} else if (sortCompany) {
console.log(sortCompany);
return [...json].sort((a, b) => {
if (a.company.toLowerCase() < b.company.toLowerCase()) {
return 1;
} else {
return -1;
}
});
} else {
return [...json].sort((a, b) => {
if (a.company.toLowerCase() > b.company.toLowerCase()) {
return 1;
} else {
return -1;
}
});
}
};
WOrking demo

How to get updated data in React

How to get updated data of react by calling the new data that will be received from another page by ajax?
How to replace new data to "Result" div.
class App extends React.Component {
constructor(props){
super(props);
this.state = {
data: [],
}
$.ajax({
url:"/test.bc",
type:"get",
success:(result)=>{
this.setState({data: eval(result)});
}
})
$(document).on('update_result',(event,startairline,classname,stops)=>{
$.ajax({
url:"/new.bc",
type:"post",
data:{
startairline:startairline,
stops:stops,
classname:classname,
},
success:(result)=>{
console.log(result)
this.setState({hasBeenChanged: true,data:eval(result)})
},
})
});
}
renderFlight(){
return this.state.data.map((item)=>{
return(<input type="hidden" value={item.total} name="total" /> )
} )}
render(){
return(<div>{this.renderFlight()}</div> )
}
}
ReactDOM.render(<App/>, document.getElementById('Result'));
I prepare you an example, using componentDidMount and fetch:
Here working
let { Table } = ReactBootstrap;
class Example extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
products: []
}
}
componentDidMount() {
console.log('componentDidMount..')
fetch('https://api.github.com/users/xiaotian/repos')
.then(response => response.json())
.then(output => {
let products = []
for (let i = 0; i < output.length; i++) {
products.push({selected:false,name:output[i].name})
}
this.setState({products},() => console.log(this.state))
})
}
render() {
return(<Table striped bordered condensed hover>
<thead>
<tr>
<th>Selected</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{this.state.products.map((item, i) => {
return (
<tr><td><input type="checkbox" checked={item.selected}/></td><td>{item.name}</td></tr>
)
})}
</tbody>
</Table>)
}
}
ReactDOM.render(
<Example />,
document.getElementById('app')
);

State changes but doesn't call render function of parent component

# Its is my parent component i have dispatch the action from child component and state changes but render function of parent component does not called .
In child component every time when i click on checkbox then i dispatch the action for updating the state in reducer and i received new state in mapStatetoprop but it does not update the UI
const propTypes = {
teamPlayers : PropTypes.array.isRequired,
matchesData: PropTypes.object.isRequired,
isfetchingTeamPlayer: PropTypes.bool.isRequired
};
class CreateTeam extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedPlayerIDs : [],
count:0,
isforward:false,
show:false,
message:''
}
this.toggleChecked = this.toggleChecked.bind(this);
this.forwardRoute = this.forwardRoute.bind(this);
this.previousRoute = this.previousRoute.bind(this);
}
componentWillMount() {
this.props.createTeam(this.props.matchID);
}
renderPlayerCard() { console.log(this.props.teamPlayers)
let count = 0;
return this.props.teamPlayers.map((player) => {
if(player.isChecked){
count+=1;
}
let mtype = '';
return (
<PlayerCard
key={player.id}
player={player}
mtype={mtype}
count={count}
selectedPlayerIDs={this.state.selectedPlayerIDs}
triggerChanges={this.toggleChecked}
/>
);
})
}
render () {
if(!this.props.isfetchingTeamPlayer && !this.props.isPlayersFetching ){
return <h1>Loading...</h1>;
}
console.log("selected5Players"+this.props.selected5Players)
return(
<div>
<div className="card">
<div className="container-grey">
<div className="timer2">
<table className="timer-table2">
{
this.props.matchesData.end_time ?
<Counter
endDate={this.props.matchesData.end_time}
/> : null
}
</table>
</div>
<table className="team-table2">
<tr className="match-team">
<td className='team-logo-box'>
<div className="white-box">
{
this.props.matchesData.hasOwnProperty('teams') ?
<img className="team-logo2" alt="Team1"
src={this.props.matchesData.teams.home_team.flag_path}/>
:null
}
</div>
</td>
<td className="team-name2 left">{this.props.matchesData.teams.away_team.short_name}</td>
<td><img className="versus2" alt="versus" src={vs}/></td>
<td className="team-name2 right">{this.props.matchesData.teams.away_team.short_name} </td>
<td className='team-logo-box'>
<div className="white-box">
<img className="team-logo2" alt="Team2"
src={this.props.matchesData.teams.away_team.flag_path}/>
</div>
</td>
</tr>
</table>
</div>
<div className="player-list">
<table className="timer-table2">
<tbody>
{this.renderPlayerCard()}
</tbody>
</table>
</div>
</div>
<div className="foot-container">
</div>
</div>
);
}
}
CreateTeam.propTypes = propTypes;
const mapStateToProps = (state,ownProps) => {
// I am getting updated state here but doesn't call the render function//
console.log("state Changes but does not call the render function of this component")
return {
matchID: ownProps.params.teamID,
selected5Players: state.matchesTeam.selected5Players,
teamPlayers: selectedCheckedplayers(state),
matchesData: state.matchesTeam.matchesData,
isfetchingTeamPlayer: state.matchesTeam.isfetchingTeamPlayer,
isPlayersFetching: state.matchesTeam.isPlayersFetching
}
};
Below the code of child component where i dispatch the action
here i dispatching the action on every checkbox (handleChange() function ) see below
const propTypes = {
player: PropTypes.object.isRequired,
};
class PlayerCard extends React.Component {
handleChange(evt,id,isSelected) {
this.props.togglePlayersChecked({"player":this.props.player,"status":!isSelected})
}
render() {
return (
<tr>
<td className="batsmen-pic">
<img alt='batsmen' className='batsmen-picture' src={this.props.player.photo_url} />
</td>
<td className="batsmen-details">
<div className="batsmen-name left">
<div className="first-name">{this.props.player.name}</div>
</div>
</td>
<td className="batsmen-checkbox-holder">
<div>
<input className="batsmen-checkbox"
type="checkbox"
onChange={event => this.handleChange(event, this.props.player,this.props.player.isChecked)}
value={this.props.player.id}
checked={this.props.player.isChecked }
/>
</div>
</td>
</tr>
);
}
}
PlayerCard.propTypes = propTypes;
const mapStateToProps = (state,ownProps) => ({
selected5Players: state.matchesTeam.selected5Players,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
togglePlayersChecked
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(PlayerCard);
below the code of Reducer function of this action
const createTeamReducer = createReducer({
[createTeam]: (state) => ({
...state,
isfetchingTeamPlayer: true,
}),
[createTeam.error]: (state, error) => ({
...state,
teamPlayerError: error,
isfetchingTeamPlayer: false,
isPlayersFetching:true
}),
[createTeam.success]: (state, payload) => {
const teamPlayers = normalize(payload.players);
const matchesData = {...payload.match_data};
const isSaved = true;
return { ...state,matchesData, teamPlayers, isSaved ,isRefreshingTeamPlayer: false };
},
[togglePlayersChecked]: (state, payload) => { console.log(payload)
let teamPlayers = state.teamPlayers;
const isSaved = false;
let selected5Players = state.selected5Players;
if (payload.status) {
if (selected5Players.length >= 5){
//alert("You can't select more then five");
return { ...state, teamPlayers,selected5Players};
}else {
const index = teamPlayers.findIndex((player) => player.id === payload.player.id);
teamPlayers[index].isChecked = true;
selected5Players.push(payload.player);
}
} else {
const index = teamPlayers.findIndex((player) => player.id === payload.player.id);
console.log(index);
var c = selected5Players.findIndex((val) => val.id === payload.player.id);
selected5Players.splice(c,1);
console.log(selected5Players)
teamPlayers[index].isChecked = false;
}
return { ...state, teamPlayers,selected5Players,isSaved};
},
[get5player]:(state,payload) => {
const selectedTeamplayer = [...state.selected5Players];
const istwoSelected = true;
console.log(selectedTeamplayer);
return { ...state, selectedTeamplayer,istwoSelected};
},
[get5Batsmen2BestBatsmen]:(state,payload) => {
let select5BatsmenAnd2BestBatsmen = [];
return { ...state, select5BatsmenAnd2BestBatsmen};
},
[deleteTeam]:(state,payload) => {
let selected5Players = [];
let select2batsmen = [];
let teamPlayers = [];
const isSaved = true;
const select5BatsmenAnd2BestBatsmen = [];
return {...state, select2batsmen, selected5Players,teamPlayers,isSaved,select5BatsmenAnd2BestBatsmen};
}
}, initialState);
export default createTeamReducer;
You might be mutating the state inside reducer instead of returning new state. Hence the changes are not detected.

Resources