my setState doesn't chance the state in the handleClick event handler.
I'm sure the handleClick works because it logs the param.
I'm kind of new to React so I must be overlooking something.
Does this mean there is something wrong with my handleClick function?
Any advice would be really appreciated!
import React from 'react';
import './Projects.css';
import Footer from '../../Components/Footer/Footer.js';
import ProjectPage from
'../../Components/ProjectPage/ProjectPage.js';
import { Redirect, Link } from 'react-router-dom';
class Projects extends React.Component {
constructor(props) {
super(props);
this.state= {
title: "kaufmann house",
content: "charles",
}
this.getImages = this.getImages.bind(this);
}
getImages() {
var VisibilitySensor = require('react-visibility-sensor');
return this.props.projectList.map((post,index) =>
<div>
<div className="projects">
<VisibilitySensor onChange={isVisible =>
this._onChange(isVisible, post.title)}>
<img key={post.id} src={post.featureImage}
className='projectImage' alt='projectImage' onClick= .
{this.handleClick.bind(this, post.content)}/>
</VisibilitySensor>
</div>
</div>
)
}
_onChange = (isVisible, param) => {
isVisible && this.setState({title: param});
};
handleClick = (param) => {
console.log(param);
this.setState({content: param});
};
render() {
return (
<div>
<Link to={{pathname: `/ProjectPage/${this.state.title}`,
state: {
info: `${this.state.content}`}
}}>{this.getImages()}</Link>
<Link to={{pathname: `/ProjectPage/${this.state.title}`,
state: {
info: `${this.state.content}`}
}}>
<Footer title={this.state.title}/>
</Link>
</div>
)
}
}
export default Projects;
this.state= {
title: "kaufmann house",
content: "charles",
}
Your state contains title and content. You have to setState like below. Otherwise, your new state will not update correctly because you replaced the whole state object.
_onChange = (isVisible, param) => {
isVisible && this.setState({
...this.state,
title: param
});
};
handleClick = (param) => {
console.log(param);
this.setState({
...this.state,
content: param
});
};
I would suggest the following changes:
1) Move var VisibilitySensor = require('react-visibility-sensor');
to the top of your file to keep your component clean
import React from 'react';
import './Projects.css';
import Footer from '../../Components/Footer/Footer.js';
import ProjectPage from
'../../Components/ProjectPage/ProjectPage.js';
import { Redirect, Link } from 'react-router-dom';
import VisibilitySensor from 'react-visibility-sensor';
2) Regarding your click handler, it is a bad practice to create handler functions using bind, because this may cause a performance issue since a new function will be created on each render. you can use an arrow function and set data-[attribute]
to add data to your component
getImages() {
//var VisibilitySensor = require('react-visibility-sensor'); remove this line
return this.props.projectList.map((post,index) => (
<div key={post.id}>
<div className="projects">
<VisibilitySensor onChange={isVisible =>
this._onChange(isVisible, post.title)}>
<img src={post.featureImage}
data-content={post.content}
className='projectImage'
alt='projectImage'
onClick={this.handleClick}/>
</VisibilitySensor>
</div>
</div>
))
}
handleClick = (e) => {
var content = e.target.dataset.content;
this.setState((state) => ({
...state,
content
}))
}
Related
I am creating a todo list where when the user clicks the checkbox "complete" that is next to the todo item, it appears in the complete component however there is a duplicate of that item that is being added as well and i am also having an issue trying to have the checkbox not appear in the completed component...
When a user creates a new todo it appears in the active component first and it has a checkbox next to it called completed and when the user clicks the checkbox it appears in the completed component
import React from 'react';
import Active from './Components/Active';
import Completed from './Components/Completed';
import Todoform from './Components/Todoform';
import './App.css';
class App extends React.Component {
state = {
items: [],
task: '',
id: 0,
completedItems: []
}
handleInput = (event) => {
this.setState({
task: event.target.value
})
}
handleSubmit = (event) => {
event.preventDefault()
const newTask = {
id: this.state.id,
title: this.state.task
}
const updatedItems = [...this.state.items, newTask]
this.setState({
items: updatedItems,
task: '',
id: this.state.id + 1
})
}
handleComplete = (newTask) => {
this.setState({completedItems: [...this.state.items, newTask]})
//console.log(this.state.items)
}
render() {
return (
<div id="main-content">
<h1>Task Lister</h1>
<Todoform
handleChange={this.handleInput}
handleSubmit={this.handleSubmit}
task={this.state.task}
/>
<Active
items={this.state.items}
handleComplete={this.handleComplete}
/>
<Completed
completedItems={this.state.completedItems}
/>
</div>
)
}
}
export default App;
import React from 'react'
class Todo extends React.Component{
state = {
checked: false
}
handleCheck = () => {
this.setState({
checked: !this.state.checked
})
}
handleClick = () => {
this.props.handlecompletedList(this.props.title)
}
render(){
const { title } = this.props
return (
<div className="ui checked checkbox">
<input type="checkbox" checked={this.state.checked} onChange={this.handleCheck}
onClick={this.handleClick}/>
<label>Completed {title}</label>
</div>
)
}
}
export default Todo;
import React from 'react'
import Todo from './Todo'
const Active = (props) => {
const { items, handleComplete } = props
return(
<div id="activeList">
<h2 className="position">Active</h2>
<ul id="tasks">
{
items.map(item => {
return(
<Todo key={item.id} handlecompletedList={handleComplete} title={item.title}/>
)
})
}
</ul>
</div>
)
}
export default Active;
import React from 'react'
import Todo from './Todo'
const Completed = (props) => {
const { completedItems } = props
return(
<div id="completedList">
<h2 className="position">Completed</h2>
<ul id="tasks">
{
completedItems.map(item => {
return(
<Todo key={item.id} title={item.title}/>
)
})
}
</ul>
</div>
)
}
export default Completed
import React from 'react';
class Todoform extends React.Component {
render(){
const {task, handleChange, handleSubmit} = this.props;
return(
<form onSubmit={handleSubmit}>
<label>Task description:</label>
<input type="text" name="name" placeholder="description" value={task} onChange={handleChange}/>
<button>Create New Task</button>
</form>
)
}
}
export default Todoform;
To hide the checkbox next to completed items you need to use Conditional Rendering. An example would be to add a prop IsCompleted to your component and use it when rendering html like this:
{this.props.isCompleted &&
<input
type="checkbox"
checked={this.state.checked}
onChange={this.handleCheck}
onClick={this.handleClick}/>
}
The duplicate item issue is probably because you use this.state.items in your handleComplete method instead of using this.state.completedItems if this is not the issue, would you mind sharing the code for the Todoform component as well?
EDIT: The item duplicates because when the handleComplete is called it copies this.state.items to the list and adds the one that you clicked on.
You should use this.state.completedItems in the handleComplete, also you are currently only sending and appending the title in the handleComplete method, you should be appending an object that has a title. The solution would be to update your handleClick method to this and update handleComplete to use this.state.completedItems:
handleClick = () => {
this.props.handlecompletedList({
title: this.props.title
});
};
I am trying to save the Range-slider value .If i click "NEXT" i want it save the value in 'Squarefeet' variable and redirect to another page .I have built a Rest API in the backend to bind and save the value in database.The code is something like this
import React, { Component } from 'react'
import Slider from 'react-rangeslider'
import Link from "next/link";
import axios from "axios";
import getConfig from "next/config";
const config = getConfig();
class Horizontal extends Component {
constructor (props, context) {
super(props, context)
this.state = {
apiUrl:config.publicRuntimeConfig.publicRuntimeConfigValue.apiUrl,
}
}
handleChangeStart = () => {
console.log('Change event started')
};
handleChange = value => {
this.setState({
value: value
})};
handleChangeComplete = () => {
console.log('Change event completed')
};
saveValue = () => {
console.log('saveValue ...', this.state);
axios.post( this.state.apiUrl+'/api/v1/LeadSurvey/save', {
'squareFeet':this.state.value,
}, {} )
};
render () {
const { value } = this.state
return (
<div>
<div className='slider' style={{ marginTop:'165px',marginLeft:'319px',width:'700px',backgroundColor:'EF5350'}} >
<Slider min={850} max={5000} value={value} onChangeStart={this.handleChangeStart}
onChange={this.handleChange}
onChangeComplete={this.handleChangeComplete}
/>
<div className='value'>{value} Squarefeet</div>
<div style={{marginTop:'86px'}}>
<Link prefetch href="/estimate"><a href="#" >
<span onChange={this.handleChange} onClick={() => this.saveValue()} >Next</span></a></Link>
</div>
</div>
</div>
)
}
}
export default Horizontal
I am not getting how to make it work to bind and save.How should i do it?
I set the state when I onChange of a input field and then have a submit function on the onClick. The onClick doesn't register at all if I click it within a second or so of the last input.
I have cut everything out of the component that I don't need and am left this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Loader from '../utils/Loader';
import './CustomMenu.scss';
import {custom_getCategory} from '../../actions/customActions';
import propsDebug from '../utils/propsDebug';
class CustomMenu extends Component {
constructor(props) {
super(props);
this.state = {
input: "",
customWords: [],
}
this.formToState = this.formToState.bind(this);
this.submit = this.submit.bind(this);
}
formToState(e) {
const {value, name} = e.target;
this.setState({[name]: value});
}
submit() {
const {input} = this.state;
this.setState( state => {
state.input = "";
console.log("newState", state);
return state;
});
}
render() {
const {input, customWords} = this.state;
return (
<div className="CustomMenu">
<input name="input" value={input} onChange={(e) => this.formToState(e)}/>
<button onClick={(e) => {
console.log("onClick");
this.submit(e);
}} style={{margin: "10px"}}>Add</button>
</div>
)
}
}
const mapStateToProps = (state, ownProps) => ({
data: state[ownProps.reduxState],
custom: state.custom,
})
export default connect(mapStateToProps, {custom_getCategory})(CustomMenu);
Any ideas how I can fix this? I feel I have used this pattern many times before without issues - I don't know what I am missing.
<button onClick={(e) => {
console.log("onClick");
this.submit(e);
}} style={{margin: "10px"}}>Add</button>
I think the bug is from the callback you passed to your onClick(). You need to return the this.submit() before you can get it to fire.
<button onClick={(e) => (
console.log("onClick");
this.submit(e);
)} style={{margin: "10px"}}>Add</button>
use an implicit return instead.
I'm having problem with sliding menu in just one item.
When I click on the config button every item shows menu. I tried to figure out something by passing props {mail.id} but I'm afraid I don't understand this.
I would like to have sliding menu just in one item -- the clicked one.
This is ConfigButton
import React, { Component } from "react";
import './Menu.css';
class ConfigButton extends Component {
render() {
return (
<button className="configButton"
onClick={this.props.onClick}
>
<i className="configButtonIcon fas fa-cog"></i>
</button>
);
}
}
export default ConfigButton;
And this is the Component which renders:
import React, { Component } from 'react';
import { NavLink, HashRouter } from 'react-router-dom';
import axios from 'axios';
import Menu from './Menu';
import ConfigButton from './ConfigButton';
const API = myAPI;
const navLinkStyle = {
textDecoration: 'none',
color: '#123e57'
};
class Emails extends Component {
constructor(props) {
super(props);
this.state = {
visible: false,
mails: []
};
this.handleMouseDown = this.handleMouseDown.bind(this);
this.toggleMenu = this.toggleMenu.bind(this);
}
handleMouseDown(e) {
this.toggleMenu();
e.stopPropagation();
}
toggleMenu() {
this.setState({
visible: !this.state.visible
});
}
componentDidMount() {
axios.get(API)
.then(response => {
const mails = response.data;
this.setState({ mails });
})
}
truncate = (text, chars = 140) =>
text.length < chars ? text : (text.slice(0, chars) + '...')
render() {
let mails = this.state.mails;
console.log(mails);
mails = mails.map(mail => {
return (
<div key={mail.id}>
<div className='mail'>
{
!mails.displayed
? <i className="notDisplayed fas fa-circle"></i>
: <i className="displayed far fa-circle"></i>
}
<HashRouter>
<NavLink
to={`/openemail/${mail.id}`}
style={navLinkStyle}
>
<ul className='ulMailWrap'>
<div className='mailHeader'>
<li>{mail.sender}</li>
<li>{mail.created}</li>
</div>
<li>{mail.subject}</li>
<li>{this.truncate(mail.message)}</li>
</ul>
</NavLink>
</HashRouter>
<ConfigButton onClick={this.handleMouseDown} />
<Menu handleMouseDown={this.handleMouseDown}
menuVisibility={this.state.visible}
/>
</div>
</div>
)
});
return (
<div>
{ mails }
</div>
);
}
}
export default Emails;
You can pass a function that will send a different parameter to the handler, depending on value of each element in the array.
Do something like this:
...
<div key={mail.id} onClick={() => this.handleOpenMenu(mail.id)}>
...
Then at the handler:
handleOpenMenu = id => {
// do different stuffs on the id you get here
this.setState({ visibleMenuId: id });
}
And then change the props you are passing to your menu component:
<Menu menuVisibility={this.state.visibleMenuId === mail.id} />
I am new to Reactjs and have been trying to create a real world website using React-Redux.
Here in my WebApp, when there is a state change in Home (Parent Component) , child component HomePage does not re-render. I will explain in detail below,
This is my Parent Component,
import React, {Component} from 'react';
import NavBar from './../components/NavBar';
import HomePage from './../components/Home/HomePage';
import {connect} from 'react-redux';
import {loadCity} from './../actions/MainPage';
import Footer from './../components/Footer';
import {instanceOf} from 'prop-types';
import {withCookies, Cookies} from 'react-cookie';
class Home extends Component {
constructor(props) {
super(props);
this.rerender = this
.rerender
.bind(this);
this.state = {
renderFlag: false
}
}
static propTypes = {
cookies: instanceOf(Cookies).isRequired
};
componentDidMount() {
this
.props
.loadCity();
}
rerender() {
this.setState({
renderFlag: !this.state.renderFlag
})
}
render() {
return (
<div>
<NavBar placelist={this.props.result} rerenderfun={() => this.rerender()}/>
<HomePage/>
<Footer/>
</div>
);
}
}
const mapStateToProps = (state) => {
return {result: state.cityReducer}
};
const mapDispatchToProps = (dispatch) => {
return {
loadCity: () => {
dispatch(loadCity());
}
};
};
export default withCookies(connect(mapStateToProps, mapDispatchToProps)(Home));
Here in my NavBar component I have to select city. When I click on city a state change occurs in HOME (Parent Component) which causes child components to re-render. But all the child components except HomePage re-render.
But if I remove connect() from HomePage, then this page gets re-rendered.
Here is my code for HomePage,
import React, {Component} from 'react';
import SearchSection from './SearchSection';
import {connect} from 'react-redux';
import {loadInfoData} from './../../actions/MainPage';
import {instanceOf} from 'prop-types';
import {withCookies, Cookies} from 'react-cookie';
import InfoSection from './../../container/Home/InfoSection ';
class HomePage extends Component {
static propTypes = {
cookies: instanceOf(Cookies).isRequired
};
componentWillMount() {
const {cookies} = this.props;
this.setState({
city: cookies.get('place')
});
}
componentDidMount() {
this
.props
.loadInfoData(this.state.city);
}
componentWillUpdate(nextProps, nextState) {
console.log('Component WILL UPDATE!');
}
render() {
return (
<div><SearchSection/> <InfoSection result={this.props.result}/>
</div>
);
}
}
const mapStateToProps = (state) => {
return {data: state.cityReducer}
};
const mapDispatchToProps = (dispatch) => {
return {
loadInfoData: (selectedCity) => {
dispatch(loadInfoData(selectedCity));
}
};
};
export default withCookies(connect(mapStateToProps, mapDispatchToProps)(HomePage));
Please help me find the issue and resolve it. I want HomePage to get re-rendered on Home state change.
UPDATE
Reducer
let defaultState = {
result: "",
info: "",
recentlyadded: ""
}
const cityReducer = (state = defaultState, action) => {
if (action.type === "GET_CITY") {
return {
...state,
result: action.result
}
} else if (action.type === "GET_INFO_DATA") {
return {
...state,
info: action.result
}
} else if (action.type === "GET_MAIN_RECENTLY_ADDED") {
return {
...state,
recentlyadded: action.result
}
} else {
return {
...state
}
}
}
export default cityReducer;
Action
import axios from 'axios';
export function loadCity() {
return (dispatch) => {
return axios
.get("**RESTAPILINK**")
.then((response) => {
dispatch(getPlace(response.data.result));
})
}
}
export function getPlace(result) {
return {type: "GET_CITY", result}
}
export function loadInfoData(selectedCity) {
var url = "**RESTAPILINK**" + selectedCity;
return (dispatch) => {
return axios
.get(url)
.then((response) => {
dispatch(getInfoData(response.data));
})
}
}
export function getInfoData(result) {
return {type: "GET_INFO_DATA", result}
}
export function loadRecentlyAdded(selectedCity) {
var url = "**RESTAPILINK**" + selectedCity;
return (dispatch) => {
return axios
.get(url)
.then((response) => {
dispatch(getRecentlyAdded(response.data));
})
}
}
export function getRecentlyAdded(result) {
return {type: "GET_MAIN_RECENTLY_ADDED", result}
}
My NavBar component
import React, {Component} from 'react';
import {
Collapse,
Navbar,
NavbarToggler,
NavbarBrand,
Nav,
NavItem
} from 'reactstrap';
import {NavLink} from 'react-router-dom';
import cityicon from '../assets/images/city/Bangalore.png';
import {instanceOf} from 'prop-types';
import {withCookies, Cookies} from 'react-cookie';
class NavBar extends Component {
constructor(props) {
super(props);
this.toggle = this
.toggle
.bind(this);
this.toggleHidden = this
.toggleHidden
.bind(this);
this.closeOverlay = this
.closeOverlay
.bind(this);
this.escFunction = this
.escFunction
.bind(this);
this.handleClick = this
.handleClick
.bind(this);
this.state = {
isOpen: false,
isHidden: true,
isLocation: false,
city: "Select your city",
classname: "city-section"
};
}
static propTypes = {
cookies: instanceOf(Cookies).isRequired
};
componentWillMount() {
const {cookies} = this.props;
let newstate = (cookies.get('place') != null)
? true
: false;
if (newstate) {
this.setState({
city: cookies.get('place')
});
}
this.setState({isLocation: newstate, isHidden: newstate});
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
toggleHidden() {
this.setState({
isHidden: !this.state.isHidden
});
}
closeOverlay() {
this.setState({
isHidden: !this.state.isHidden
});
}
escFunction(event) {
if (event.keyCode === 27) {
if (this.state.isHidden === false) {
this.closeOverlay();
}
}
}
handleClick(selectedCity) {
const {cookies} = this.props;
cookies.set("place", selectedCity);
this
.props
.rerenderfun();
if (this.state.isLocation) {
this.setState({
isHidden: !this.state.isHidden,
city: cookies.get('place')
})
} else {
this.setState({
isLocation: !this.state.isLocation,
isHidden: !this.state.isHidden,
city: cookies.get('place')
})
}
}
render() {
var overlayClass = 'city-section';
if (!this.state.isLocation) {
overlayClass += " city-section visible"
} else if (this.state.isHidden) {
overlayClass += " city-section hidden";
} else {
overlayClass += " city-section visible"
}
return (
<div>
<Navbar className="navbar navbar-expand-md navbar-dark" light expand="md">
<NavbarBrand href="/">
<i className="fa fa-graduation-cap mr-2" aria-hidden="true"></i>Company
</NavbarBrand>
<NavbarToggler onClick={this.toggle}/>
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto" navbar>
<NavItem>
<NavLink className="nav-link" to="/">Favourites</NavLink>
</NavItem>
<NavItem>
<NavLink to="/" className="nav-link">Login</NavLink>
</NavItem>
<NavItem>
<a className="nav-link" onClick={() => this.toggleHidden()}>
<i className="fa fa-map-marker mr-2" aria-hidden="true"></i>{this.state.city}</a>
</NavItem>
</Nav>
</Collapse>
</Navbar>
<div className={overlayClass} onClick={this.closeOverlay}>
<div
className="city-content py-5"
onClick={(e) => {
e.stopPropagation();
}}>
<div className="container">
<div className="row text-center">
<div className="col-12">
<h4 className="text-secondary">Select your City</h4>
</div>
</div>
{Object
.entries(this.props.placelist.result)
.map(([k, value]) => {
return (
<div className="row text-center mt-5" key={k}>
<div className="col-md-2 offset-md-5">
<div
className="card border-0 location-card"
onClick={() => {
this.handleClick(value.city)
}}>
<div className="card-body">
<img className="location-img" src={cityicon} alt="bangalore"/>
<p className="font-weight-bold mt-3 mb-0">{value.city}</p>
</div>
</div>
</div>
</div>
)
})}
<div className="row text-center pt-5">
<div className="col-12">
<h6 className="text-secondary mt-3 font-italic">Currently we are only in Bangalore</h6>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default withCookies(NavBar);
you need to pass loadInfoData bind to dispatch to navbar component. You can write mapdispatchtoProps for navbar also .
handleClick(selectedCity) {
const {cookies, loadInfoData} = this.props;
loadInfoData(selectedCity)
cookies.set("place", selectedCity);
this
.props
.rerenderfun();
if (this.state.isLocation) {
this.setState({
isHidden: !this.state.isHidden,
city: cookies.get('place')
})
} else {
this.setState({
isLocation: !this.state.isLocation,
isHidden: !this.state.isHidden,
city: cookies.get('place')
})
}
}
// add below code for navbar component
const mapDispatchToProps = (dispatch) => {
return {
loadInfoData: (selectedCity) => {
dispatch(loadInfoData(selectedCity));
}
};
};
export default connect(null, mapDispatchToProps)(NavBar)
First of all it's hard to provide a conclusive answer with just the codes you shared, I mean how you wrote the reducers and all.
Nevertheless, I can suggest some best practices regarding react and redux architecture. Like here in your case does both the parent and child component needs to connect to redux store? as the parent component(Home) is connected and it re-renders so the child component(HomePage) can just receive values with props.
For further studies I suggest going through the following.
https://github.com/reactjs/redux/issues/585
https://redux.js.org/docs/Troubleshooting.html
personal suggestions or descriptions:
Thing is there are a lot of best practices and standards and none are absolute or one to rule them all type. It solely depends on your needs and application architecture. But what you need is to grasp some concepts like smart and dumb components or container and presentational components, lifting the state up(react docs explains it best). Try to get you head around these concepts. React being a very well engineered library problems like these wont even appear if you architect your application cleverly.
Knowing about the whole application ahead and being able to divide that into many components with as small footprints as possible is the key here I think.
For start you can first design whole state tree of the application (object structure) if not possible then up to some extent and design the component hierarchy by looking at that state tree, you will find that everything is falling into places.