I new in ReactJS and i have one few question. I defined function showModal and but console.log() and
this.setState({show:!this.state.show});.
And after that I applied
this function onClick event for div element inside map function.
1st question: When I click on div element showModal work but in console I don't see my console.log.
2nd question: I want to make when you click on one div element it must add/show few new div elements but only for one div element (on which I clicked). But now when I click on one div element it add/show new elements for all div elements which had this showModal function.
How can i fix this
import React, { Component } from "react";
import Modal from '../components/modal/form'
const DEFAULT_QUERY = 'redux';
const PATH_BASE = 'URL which work correct';
class Actions extends React.PureComponent{
constructor(){
super();
this.state = {
result:null,
show:false,
helpId:null
};
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.showModal = this.showModal.bind(this);
this.handleClickFromParent = this.handleClickFromParent.bind(this);
this.onClose = this.onClose.bind(this);
}
onClose = e => {
this.setState({ show: false});
}
handleClickFromParent = e => {
this.setState({show: !this.state.show});
}
showModal = e => {
console.log('BABE');
this.setState({show: !this.state.show})
};
setSearchTopStories(result) {
this.setState({ result });
};
componentDidMount() {
fetch(`${PATH_BASE}`)
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
.catch(error => error);
};
render(){
const { searchTerm, result } = this.state;
console.log('* Actions Pure*');
console.log(result);
console.log('=');
return(
<div>
{
(result !== null) ?
result.map(
(item,index) =>
<div>
<div onClick={()=>this.showModal()}>{item.name}</div>
<Modal
id = {index}
handleClickFromParent {this.handleClickFromParent}
item = {[item]}
show = {this.state.show}
onClose = {this.onClose}>
YOLO
</Modal>
</div>
)
: null
}
</div>
)
}
}
export default Actions;
While selecting u can pass the item on method, and on click u can set the item value. Please check the below code.
Demo:
https://codesandbox.io/s/stackoverflowmodal-19i36
this.state = {
result: null,
show: false,
selectedItem:null,
helpId: null
};
//
showModal = (selectedItem) => {
this.setState({
show: !this.state.show,
selectedItem
});
};
//
class Actions extends React.PureComponent {
constructor() {
super();
this.state = {
result: null,
show: false,
selectedItem: null,
helpId: null
};
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.showModal = this.showModal.bind(this);
this.handleClickFromParent = this.handleClickFromParent.bind(this);
this.onClose = this.onClose.bind(this);
}
onClose = e => {
this.setState({
show: false
});
};
handleClickFromParent = e => {
this.setState({
show: !this.state.show
});
};
showModal = selectedItem => {
this.setState({
show: !this.state.show,
selectedItem
});
};
setSearchTopStories(result) {
this.setState({ result });
}
componentDidMount() {
fetch(`${PATH_BASE}`)
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
.catch(error => error);
}
render() {
const { searchTerm, result, selectedItem } = this.state;
return (
<div>
{result && result.length
? result.map((item, index) => (
<div>
<div onClick={() => this.showModal(item)}>{item.name}</div>
</div>
))
: null}
{selectedItem && (
<Modal
id={index}
handleClickFromParent={this.handleClickFromParent}
item={[selectedItem]}
show={this.state.show}
onClose={this.onClose}
>
YOLO
</Modal>
)}
</div>
);
}
}
export default Actions;
Related
I cant figure out why my Login component is not re-rendering what is on the page. The state is changed to loginShow after I click on a "Log In" button on top of my form.
import React from 'react';
import SignUp from '../SignUp';
import Cards from '../Articles/Cards';
import Login from '../LogIn';
export default class Gecko extends React.Component {
constructor(props) {
super(props);
this.state = { requestedPostsThatWeGotFromGecko: null, }
this.clickMe = this.clickMe.bind(this)
this.state = {
loginStatus: "loginHide",
authType: "signup"
};
this.loginClose = this.loginClose.bind(this);
this.loginOpen = this.loginOpen.bind(this);
}
loginClose(e) {
e.stopPropagation();
this.setState({
loginStatus: "loginHide"
});
}
loginOpen() {
this.setState({
loginStatus: "loginShow"
});
}
clickMe = () => {
const {requestedPostsThatWeGotFromGecko} = this.state;
this.setState({ requestedPostsThatWeGotFromGecko: !requestedPostsThatWeGotFromGecko })
}
/* mainLogIn = () => {
const {loginStatus} = this.state;
this.setState({ loginStatus: 'loginHide' })
} */
render() {
const { requestedPostsThatWeGotFromGecko } = this.state;
const { loginStatus } = this.state;
console.log(loginStatus);
// const { authType } = this.state;
// if (loginStatus === "loginHide") return <SignUp login={() => this.clickMe()} />
// if (requestedPostsThatWeGotFromGecko.props === true) return <Cards />
if (loginStatus !== "loginShow") return (
<div className="gecko">
{requestedPostsThatWeGotFromGecko ? (
<Cards />
): (
<SignUp login={() => this.clickMe()} />
)
}
</div>
);
if (loginStatus === "loginShow") return <Login />
}
}
I believe the issue lies where I am trying to set what is being displayed using the conditional operator at the end of the code above. I have included where I am setting the state and props from my other components in the code below.
const onClick = () => {
this.props.login();
console.log('rich');
}
const mainLogIn = () => {
this.setState({
loginStatus: "loginShow"
});
console.log('rich1');
console.log(this.state);
}
const onClick = () => {
this.props.login();
console.log('rich');
}
const mainSignUp = () => {
this.props.signup();
this.setState({
loginStatus: "loginHide"
});
console.log('rich2');
}
I am loading a child component on parent component in React.js. With a click on the button, data will be pass to child component through props, and child component will map through that data and render on screen. I am getting data from localstorage and processing its data structure to child component.
But, the issue is when I click on the button and data is being passed and rendered, the button is shown and after the child component is rendered that shows up. I need the loading spinner when I click on the button and it disappears and shows the actual component.
I have tried methods like loading: false in the state but to no avail.
Thanks for your help
import ShowPatientAppointments from './ShowPatientAppointments.component';
class PatientAppointmnent extends Component {
state = {
doctorSlots: null,
timingSlot: null,
bookDay: null,
bookTime: null,
hasTiming: false,
}
getSlots = () => {
let slot = [];
let time = [];
for (let i=0; i< localStorage.length; i++) {
let key = localStorage.key(i);
let value = JSON.parse(localStorage[key]);
slot.push(key);
time.push(value);
this.setState({doctorSlots: slot, timingSlot: time});
}
console.log(this.state.doctorSlots, this.state.timingSlot);
}
render() {
const { doctorSlots, timingSlot, hasTiming } = this.state;
return(
<div>
<button onClick={this.getSlots} className='button'>Show me dates</button>
{doctorSlots === null ? <p></p> : <PatientSelectDay props={doctorSlots} timing={timingSlot} getTimings={this.getTiming} />}
</div>
)
}
}
export default PatientAppointmnent;
class PatientSelectDay extends Component {
state = {
options: [...this.props.props].map(obj => {
return {value: `${obj}`, label: `${obj}`}
}),
timingOptions: [...this.props.timing],
open_id: [],
part_id: '',
doctorDay: 'day',
doctorTiming: 'timing',
}
changeSingleHandler = e => {
this.setState({ part_id: e ? e.value : '' });
};
changeHandler = e => {
let add = this.state.open_id;
add.push(e.map(x => x.value));
this.setState({ open_id: e ? add : [] });
};
saveState = (option) => {
...save selected options
}
render() {
const {options, timingOptions} = this.state;
return (
<div>
<div className='carousel'>
{options.map((option, index) => {
const timing = timingOptions[index].map(obj => {
return {value: `${obj}`, label: `${obj}`}});
return(
<div key={index}>
<Select
name="open_id"
value={option}
onChange={this.changeSingleHandler}
options={option}
className='select'
/>
<Select
name="open_id"
value={this.state.open_id}
onChange={this.changeHandler}
options={timing}
className='select'
/>
<button onClick={() => this.saveState(option)} className='button-left'>Select Days</button>
</div>
)
})}
</div>
</div>
)
}
}
export default PatientSelectDay;
You need to update your code adding a loading state variable.
class PatientAppointmnent extends Component {
state = {
doctorSlots: null,
timingSlot: null,
bookDay: null,
bookTime: null,
hasTiming: false,
loading: false
}
getSlots = () => {
let slot = [];
let time = [];
this.setState({
loading: true
})
for (let i=0; i< localStorage.length; i++) {
let key = localStorage.key(i);
let value = JSON.parse(localStorage[key]);
slot.push(key);
time.push(value);
this.setState({doctorSlots: slot, timingSlot: time, loading: false});
}
console.log(this.state.doctorSlots, this.state.timingSlot);
}
renderButton = () => {
const { doctorSlots, timingSlot, loading } = this.state;
if(doctorSlots === null && timingSlot === null) {
return <div>
{loading ? <p>Loading...</p> : <button onClick={this.getSlots} className='button'>Show me dates</button>}
</div>
}
return null;
}
render() {
const { doctorSlots, timingSlot, hasTiming, loading } = this.state;
return(
<div>
{this.renderButton()}
{doctorSlots === null ? <p></p> : <PatientSelectDay props={doctorSlots} timing={timingSlot} getTimings={this.getTiming} />}
</div>
)
}
}
export default PatientAppointmnent;
class PatientSelectDay extends Component {
state = {
options: [...this.props.props].map(obj => {
return {value: `${obj}`, label: `${obj}`}
}),
timingOptions: [...this.props.timing],
open_id: [],
part_id: '',
doctorDay: 'day',
doctorTiming: 'timing',
}
changeSingleHandler = e => {
this.setState({ part_id: e ? e.value : '' });
};
changeHandler = e => {
let add = this.state.open_id;
add.push(e.map(x => x.value));
this.setState({ open_id: e ? add : [] });
};
saveState = (option) => {
...save selected options
}
render() {
const {options, timingOptions} = this.state;
return (
<div>
<div className='carousel'>
{options.map((option, index) => {
const timing = timingOptions[index].map(obj => {
return {value: `${obj}`, label: `${obj}`}});
return(
<div key={index}>
<Select
name="open_id"
value={option}
onChange={this.changeSingleHandler}
options={option}
className='select'
/>
<Select
name="open_id"
value={this.state.open_id}
onChange={this.changeHandler}
options={timing}
className='select'
/>
<button onClick={() => this.saveState(option)} className='button-left'>Select Days</button>
</div>
)
})}
</div>
</div>
)
}
}
export default PatientSelectDay;
I have a TaskList. I'm trying to display a child component TaskEdit (edit box for the task) several components lower, onClick of a button.
TaskEdit has a state variable hidden, set to true by default. How do I change the state inside the child component from within the child component?
React documentation mentions a forwardRef() function in order to reference the child component, but I haven't any luck so far with it.
Here's my code:
import TaskEdit from '../TaskEdit';
class TasksBase extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
tasks: [],
};
this.markTaskAsCompleted = this.markTaskAsCompleted.bind(this);
this.deleteTask = this.deleteTask.bind(this);
this.incrementDate = this.incrementDate.bind(this);
this.toggleTaskEdit = this.toggleTaskEdit.bind(this);
}
componentDidMount() {
this.onListenForTasks();
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { tasks, loading } = this.state;
return (
<div>
{loading && <div>Loading ...</div>}
{tasks ? (
<TaskList tasks={tasks}
handleToggleEdit={this.toggleTaskEdit}
handleMarkCompleted={this.markTaskAsCompleted}
handleDeleteTask={this.deleteTask}
taskEditElement={this.editTaskElement}
/>
):(
<div>There are no tasks ...</div>
)}
</div>
);
}
onListenForTasks() {
this.setState({ loading: true });
this.unsubscribe = this.props.firebase
.tasks()
.orderBy('importance', 'desc')
.orderBy('urgency', 'desc')
.orderBy('created', 'desc')
.onSnapshot(snapshot => {
if (snapshot.size) {
let tasks = [];
snapshot.forEach(doc =>
tasks.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
tasks: tasks,
loading: false
});
} else {
this.setState({ tasks: null, loading: false });
}
});
}
markTaskAsCompleted(task){
// Some code
}
deleteTask(id){
// Some code
}
toggleEditTask(){
this.editTaskElement.current.toggle();
}
}
const Tasks = withFirebase(TasksBase);
const TaskList = ({ tasks, handleToggleEdit, handleMarkCompleted, handleDeleteTask }) => (
<ul className="tasks">
{tasks.map( task => (
<Task key={task.uid}
task={task}
handleToggleEdit={handleToggleEdit}
handleMarkCompleted={handleMarkCompleted}
handleDeleteTask={handleDeleteTask}
/>
))}
</ul>
);
const Task = ({ task, handleToggleEdit, handleMarkCompleted, handleDeleteTask}) => (
(!task.completed && !task.obsolete && !task.waitingForDependencies) && (!task.start || task.start.toMillis() <= Date.now()) &&
<li className="task">
<strong>{task.userId}</strong> {task.name}
{ task.estimate &&
<span>{task.estimate.amount + task.estimate.unit}</span>
}
<div className="icons">
<FontAwesomeIcon icon="edit" onClick={() => handleToggleEdit(task)} />
<FontAwesomeIcon icon="check-circle" onClick={() => handleMarkCompleted(task)} />
<FontAwesomeIcon icon="times-circle" onClick={() => handleDeleteTask(task.uid)}/>
</div>
<TaskEdit task={task} />
</li>
);
const condition = authUser => !!authUser;
export default compose(
withEmailVerification,
withAuthorization(condition),
)(Tasks);
TaskEdit:
import React, { Component } from 'react';
import { withFirebase } from '../Firebase';
const INITIAL_STATE = {
hidden: true,
};
class TaskEditBase extends Component {
constructor(props) {
super(props);
this.state = { ...INITIAL_STATE };
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
console.log("name " + name);
console.log("value " + value);
this.setState({
[name]: value
});
}
toggle(){
this.setState({
hidden: !this.prevState.hidden
})
}
onSumbitTaskEdit = (event) => {
event.preventDefault();
};
render(){
return(
!this.state.hidden &&
<div className="task-edit">
<form onSubmit={this.onSumbitTaskEdit}>
<div>A bunch of inputs</div>
<input type="submit" value="Edit"/>
</form>
</div>
)
}
}
const TaskEdit = withFirebase(TaskEditBase);
export default TaskEdit;
I have a tab React component which I'm testing with Enzyme and Jest.
here's the component:
class TabbedArea extends React.Component{
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this);
this.state = {
activeIndex: this.props.activeIndex || 0
};
}
componentWillReceiveProps(nextProps){
if (this.state.activeIndex !== nextProps.activeIndex) {
this.setState({
activeIndex: nextProps.activeIndex
});
}
}
handleClick(index, e) {
e.preventDefault();
this.setState({
activeIndex: index
});
}
tabNodes() {
return (_.map(this.props.children, (child, index) => {
let className = classNames({'active': this.state.activeIndex === index});
return (
<li key={index} onClick={(e) => this.handleClick(index, e)}>
<a className={className} href="#">{child.props.display}</a>
</li>
)
}
)
)
}
contentNodes() {
return (
_.map(this.props.children, (child, index) => {
if(this.state.activeIndex === index) {
return (
<div className="TabPane" key={index}>
{child.props.children}
</div>
)
}
}
)
)
}
render() {
return (
<div className={`${styles.ParcelResultsTrackingHistory} col-md-12`}>
<ul className="nav nav-tabs" id="navTabs">
{this.tabNodes()}
</ul>
<section>
{this.contentNodes()}
</section>
</div>
);
}
}
export default TabbedArea;
and here's my test:
describe('Given the Tabbed Area component is rendered', () => {
describe('Initial Snapshots', () => {
let component
beforeEach(() => {
component = shallow(<TabbedArea />)
})
it('should be as expected', () => {
expect(shallowToJson(component)).toMatchSnapshot()
})
})
describe('I click on the second tab', () => {
let component
let tab2
component = shallow(<TabbedArea/>)
tab2 = component.find('ul li').at(1);
tab2.simulate('click');
describe('the content of the second tab is rendered', () => {
component.update();
it('should match the snapshot', () => {
expect(shallowToJson(component)).toMatchSnapshot()
});
});
});
describe('Clicking on the tab', () => {
const wrapper = mount(<TabbedArea/>)
const handleClick = jest.spyOn(wrapper.instance(), 'handleClick');
wrapper.update();
const tab = wrapper.find('ul#navTabs li').first()
tab.simulate('click')
it('will call handleClick', () => {
expect(handleClick).toBeCalled();
});
});
})
The snapshot tests run fine but when I try to test the handleClick it fails with: Method “simulate” is only meant to be run on a single node. 0 found instead. Any idea why can't find the node? I've tried finding the li by id but got the same error.
thanks
Isn't it because you are rendering <TabbedArea> with no children. tabNodes method loops over this.props.children which in your test is empty.
I can't seem to pass this handler correctly. TabItem ends up with undefined for onClick.
SearchTabs
export default class SearchTabs extends Component {
constructor(props) {
super(props)
const breakpoints = {
[SITE_PLATFORM_WEB]: {
displayGrid: true,
autoFocus: true,
},
[SITE_PLATFORM_MOBILE]: {
displayGrid: false,
autoFocus: false,
},
};
this.state = {
breakpoints,
filters: null,
filter: null,
isDropdownOpen: false,
selectedFilter: null,
tabs: null,
};
this.tabChanged = this.tabChanged.bind(this);
this.closeDropdown = this.closeDropdown.bind(this);
}
... more code
createTabs(panels) {
if(!panels) return;
const tabs = panels.member.map((panel, idx) => {
const { selectedTab } = this.props;
const { id: panelId, headline } = panel;
const url = getHeaderLogo(panel, 50);
const item = url ? <img src={url} alt={headline} /> : headline;
const classname = classNames([
searchResultsTheme.tabItem,
(idx === selectedTab) ? searchResultsTheme.active : null,
]);
this.renderFilters(panel, idx, selectedTab);
return (
<TabItem
key={panelId}
classname={classname}
idx={idx}
content={item}
onClick={this.tabChanged(idx, headline)}
/>
);
});
return tabs;
}
tabChanged(idx, headline) {
const { selectedTab } = this.props;
const { selectedFilter } = this.state;
const selectedFilterIdx = _.get(selectedFilter, 'idx', null);
if (selectedTab !== idx) {
this.props.resetNextPage();
this.props.setTab(idx, selectedFilterIdx, headline);
this.closeDropdown();
}
}
render() {
// const { panels, selectedTab } = this.props;
// if (!panels || panels.length === 0) return null;
//
//
// const { tabs, selectedTab } = this.props;
return (
<div>
<ul>{this.state.tabs}</ul>
</div>
);
}
}
export const TabItem = ({ classname, content, onClick, key }) => (
<li key={key} className={`${classname} tab-item`} onClick={onClick} >{content}</li>
);
so in TabItem onClick={onClick} ends up with undefined for onClick.
More info
here's how this used to work, when this was a function in the parent Container:
// renderDefaultTabs() {
// const { panels, selectedTab } = this.props;
//
// if (!panels || panels.length === 0) return;
//
// let filter = null;
//
// const tabs = panels.member.map((panel, idx) => {
// const { id: panelId, headline } = panel;
// const url = getHeaderLogo(panel, 50);
// const item = url ?
// <img src={url} alt={headline} /> : headline;
// const classname = classNames([
// searchResultsTheme.tabItem,
// (idx === selectedTab) ? searchResultsTheme.active : null,
// ]);
//
// filter = (idx === selectedTab) ? this.renderFilters(panel) : filter;
//
// return (
// <li
// key={panelId}
// className={classname}
// onClick={() => {
// this.tabChanged(idx, headline);
// }}
// >
// {item}
// </li>
// );
// });
So I extracted that out to that SearchTabs including moving the tabChange d method to my new SearchTabs component. And now in the container the above now does this:
renderDefaultTabs() {
const {
onFilterClick,
panels,
resetNextPage,
selectedTab,
selectedFilter,
isDropdownOpen,
} = this.props;
return (<SearchTabs
panels={panels}
...
/>);
}
Note: renderDefaultTabs() is sent as a prop to in the render() of the container and the Search calls it back thus rendering it in the Search's render():
Container
render() {
return (
<Search
request={{
headers: searchHeaders,
route: searchRoute,
}}
renderTabs={this.renderDefaultTabs}
renderSearchResults={this.renderSearchResults}
handleInputChange={({ input }) => {
this.setState({ searchInput: input });
}}
renderAltResults={true}
/>
);
}
Search is a shared component our apps use.
Update
So I mentioned that the Container's render() passes the renderDefaultTabs function as a prop to <Search />. Inside <Search /> it ultimately does this: render() { <div>{renderTabs({searchResults})}</div>} which calls the container's renderDefaultTabs function which as you can see above, ultimately renders
So it is passing it as a function. It's just strange when I click a TabItem, it doesn't hit my tabChanged function whatsoever
Update
Christ, it's hitting my tabChanged. Errr..I think I'm good. Thanks all!
onClick={this.tabChanged(idx, headline)}
This is not a proper way to pass a function to child component's props. Do it like (though it is not recommended)
onClick={() => this.tabChanged(idx, headline)}
UPDATE
I want to add more explanation. By onClick={this.tabChanged(idx, headline)}, you are executing tabChanged and pass its returned value to onClick.
With your previous implementation: onClick={() => { this.tabChanged(idx, headline); }}, now onClick will be a function similar to:
onClick = {(function() {
this.tabChanged(idx, headline);
})}
So it works with your previous implementation.
With your new implementation, onClick={() => this.tabChanged(idx, headline)} should work