Calling state hook from outside its component - reactjs

When I run my current code it produces a button for each post in a local JSON file and if I click on the button it expands/contracts. I want to have a button that opens or closes all the posts but I'm unsure how to achieve this, my logic is that since externalData is an array of Post components, handleClick should simply be calling its own function but I get the error element.setOpenStatus is not a function when I press the Open All button.
function Post(props) {
const [openStatus, setOpenStatus] = useState(true);
if (openStatus) {
return (
<button onClick={() => setOpenStatus(false)}>
<p>Title: {props.post.title}</p>
<p>User: {props.post.user}</p>
<p>Time: {props.post.time}</p>
<p>Content: {props.post.content}</p>
<hr />
</button>
);
}
return (
<button onClick={() => setOpenStatus(true)}>
<p>Title: {props.post.title}</p>
<p>User: {props.post.user}</p>
<p>Time: {props.post.time}</p>
<hr />
</button>
);
}
class AllPosts extends React.Component {
constructor(props) {
super(props);
this.state = {
externalData: [],
};
this.handleClick = this.handleClick.bind(this);
this.renderPost = this.renderPost.bind(this);
}
renderPost(element) {
return (
<Post
key={element.title}
post={element}
/>
)
}
checkIfExisting(post, key, value) {
return post[key] === value;
}
// HERE things are going wrong
handleClick() {
this.state.externalData.forEach(element => {
element.setOpenStatus({ openStatus: false });
});
}
render() {
this.props.data.forEach((element) => {
if (!this.state.externalData.some(
(post) => this.checkIfExisting(post, "key", element.title)
)) {
this.state.externalData.push(this.renderPost(element))
}
})
return (
<div>
<h1>using local json file</h1>
<button onClick={this.handleClick}>Open All</button>
<div>
{this.state.externalData}
</div>
</div>
);
}
}
export default AllPosts;

Related

Cannot read properties of undefined - reactjs

Please help. I am new to react and I'm trying to toggle an accordion by using state but I'm getting an error on the onClick portion. Cannot read properties of undefined (reading 'toggleAccordion'). I'm confused to what I should do.
Any help would be appreciated. Thank you!
export class Accordion extends Component {
constructor(props) {
super(props);
this.state = {
isActive: false
}
this.toggleAccordion = this.toggleAccordion.bind(this);
this.renderAccordion = this.renderAccordion.bind(this);
}
toggleAccordion = () => {
this.setState({ isActive: !this.state.isActive})
}
renderAccordion = () =>
{
const { items } = this.props;
const { isActive } = this.state;
const accordionItems = items.map(function(item) {
return (
<div>
<button className={styles.accordion} onClick={this.toggleAccordion()}>
{item.title}
<span style={{float: "right"}}>{isActive ? '-' : '+'}</span>
</button>
{isActive && <div className={styles.content} dangerouslySetInnerHTML={{__html: item.content}}></div>}
<br/><br/>
</div>
)
})
return (
{accordionItems}
)
}
render() {
return(
<React.Fragment>
{this.renderAccordion()}
</React.Fragment>
)
}
}
In React you need to provide a reference to the function in event handlers, instead of calling the function. Try (note the parenthesis) :
<button className={styles.accordion} onClick={() => this.toggleAccordion()}>
or
<button className={styles.accordion} onClick={this.toggleAccordion}>
instead of
<button className={styles.accordion} onClick={this.toggleAccordion()}>
See https://reactjs.org/docs/handling-events.html#gatsby-focus-wrapper for more details on how React handle events.

unable to display state variable inside <h1>

export default class player extends React.Component {
constructor(...args) {
super(...args);
this.state = {
shoot: 0
};
}
shootis the variable i'm trying to change in the function shooter, and display later in <h1>.
shooter() {
this.setState({ shoot: Math.floor(Math.random() * Math.floor(3)) });
console.log("hello");
}
render() {
return (
<div>
<h1>{this.state.shoot}</h1>
<button onClick={() => this.shooter}>shoot it</button>
</div>
);
}
}
the <h1> is not changing as the state changes, won't the state change as shooter() fires? and doesn't it update the <h1>.
any help much appreciated. :-)
Change the line
<button onClick={() => this.shooter}>shoot it</button>
To
<button onClick={() => this.shooter()}>shoot it</button>
Bind your class method shooter in your constructor, in order to use it like this onClick={this.shooter}.
You can find a further explanation here.
export default class player extends React.Component {
constructor(...args) {
super(...args);
this.state = {
shoot: 0
};
this.shooter = this.shooter.bind(this);
}
render() {
return (
<div>
<h1>{this.state.shoot}</h1>
<button onClick={this.shooter}>shoot it</button>
</div>
);
}
}

Multiple buttons do the same

I am building an app in React, that is connected to an API I have written before. Buttons are renderizing but all of them change at the same time. I need advice about how can I write my code in order to separate the functionality.
My app renderize with a .map the same number of Buttons as appointments which is an array. All of them change when this.state.shown change but I need to separate all the buttons in order to only show the one that I clicked. Right now, when I clicked in one of them, this.state.shown change its value so all the buttons change because all depends of the same variable. I am looking for advices about how I can separate this.
class AppointmentsList extends Component {
constructor(props) {
super(props);
this.state = {
appointments: [],
isLoading: false,
shown: false, //Variable to know if a button need to change and render the component
customerUp: false
}
this.toggleCustomer = this.toggleCustomer.bind(this);
//this.showCustomer = this.showCustomer.bind(this);
}
toggleCustomer() {
this.setState({
shown: !this.state.shown
})
} //This function change the value of shown when a Button is clicked.
render() {
const {appointments, isLoading} = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown ? <CustomerView id={app.customer.id} /> : null }
</div>
)}
</div>
)
}
How can I reorganize my code in order to render the Buttons separately?
Thanks in advance.
Method 1: You can make shown state a object like:
state = {
shown:{}
}
toggleCustomer(id) {
const updatedShownState = {...this.state.shown};
updatedShownState[id] = updatedShownState[id] ? false : true;
this.setState({
shown: updatedShownState,
})
} //This function change the value of shown when a Button is clicked.
render() {
const {appointments, isLoading} = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown[app.customer.id] ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer(app.customer.id) }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown[app.customer.id] ? <CustomerView id={app.customer.id} /> : null }
</div>
)}
</div>
)
}
Method 2: Make a separate component for Button and Customer
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<Appointment key = {app.id} app = {app} />
)}
</div>
)
}
class Appointment extends Component {
state = {
shown: false,
}
toggleCustomer() {
this.setState({
shown: !this.state.shown
})
}
render() {
const { app } = this.props;
return (
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown ? <CustomerView id={app.customer.id} /> : null }
</div>
)
}
}
Let me know if it works and the method you prefer.
You can create a separate component for your button (buttonComponent) inside your AppointmentsList component and pass the shown has props and the in componentDidMount of buttonComponent copy the props to the state of buttonComponent.
This way each button will have its own state, which manages shown.
Button component:
import react from 'react';
interface buttonComponentProps{
shown: boolean;
}
interface buttonComponentState{
shown: boolean;
}
class buttonComponent extends react.Component<buttonComponentProps,{}>{
constructor(props:buttonComponentProps){
super();
this.state{
shown:props.shown
}
}
....
}
export default buttonComponent;

React component opens all child states

I have a list of courses with student icon on them. When clicked on student icon it opens a modal and displays all assigned students on the course.
The problem I am having is when I click on one of the course to open the modal, it opens the modal for all the other courses. I know its to do with the state behaviour but I can't seem to figure out how best to tackle this problem.
Following is my code:
class CourseDetails extends React.Component {
constructor(props) {
super(props);
autobind(this);
this.state = { openStudentsAssignedToCourseModal: false };
}
closeStudentsAssignedToCourseModal() {
this.setState({ openStudentsAssignedToCourseModal: false });
}
render() {
const { store } = this.props;
const { openStudentsAssignedToCourseModal } = this.state;
return store.allCourses.map((course) => {
return (
<Container key={course.id}>
<p>{course.name}</p>
<UsersIcon
size={25}
onClick={() => {
if (course.listOfStudents.length > 0)
this.setState({
openStudentsAssignedToCourseModal: true
});
}}
/>
{openStudentsAssignedToCourseModal && (
<StudentsOnCourseModal
course={course}
isOpen
close={() => {
this.closeEmployeesAssignedModal();
}}
/>
)}
</Container>
);
});
}
}
Modal:
class StudentsOnCourseModal extends React.Component {
constructor() {
super();
autobind(this);
}
render() {
const { course, isOpen, close } = this.props;
const s = course.listOfStudents.length === 1 ? '' : 's';
return (
<Modal
isOpen={isOpen}
close={close}
width="large"
bgDismiss={false}
>
<ModalHeader>
<h2>Assigned students</h2>
</ModalHeader>
<ModalBody>
<p>
There {s === '' ? 'is' : 'are'}{' '}
<b>{course.listOfStudents.length}</b> student{s}{' '}
currently assigned to the course <b>{course.name}</b>.
</p>
<StudentsContainer>
{course.listOfStudents.map(student => (
<StudentItem key={student.id}>
<StudentCard
name={student.name}
link={`/student-profile/${
student.id
}/personaldetails`}
imageHref={
student._links.image
? student._links.image.href
: undefined
}
/>
</StudentItem>
))}
</StudentsContainer>
<OutlineButton
onClick={e => {
e.preventDefault();
close();
}}
>
Close
</OutlineButton>
</ModalBody>
</Modal>
);
}
}
I wasn't capturing the selected course so it was opening all of them. Fixed it by introducing a new state to capture the selected value and passed that into the modal.

React button: how to make it change onClick

I'm learning React and I'm trying to make a button which will change on click. It must be ether "Succeed" or "Not succeed" depending on the server's answer. This is what I've done so far. My question is - what the handleClick function must do? Should I use transition by toggling classes?
Thanks!
class Btn extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handlenClick.bind(this);
}
handleClick() {
???
}
render() {
const succeed = (
<div>
<ButtonToolbar>
<Button bsStyle="primary" bsSize="large">Succeed</Button>
</ButtonToolbar>
</div>
)
const notsucceed = (
<div>
<ButtonToolbar>
<Button bsStyle="primary" bsSize="large">Not succeed</Button>
</ButtonToolbar>
</div>
)
return (
<div onClick={this.handleClick.bind(this)}>
{this.state ? succeed : notsucceed}
</div>
)
}
};
You need to implement the state.
For example: https://codesandbox.io/s/313vmr23k6
class Btn extends React.Component {
constructor(props) {
super(props);
this.state = {
succeed: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(previousState => {
return {
succeed: !previousState.succeed
};
});
}
render() {
const succeed = (
<div>
<button bsStyle="primary" bsSize="large">Succeed</button>
</div>
)
const notsucceed = (
<div>
<button bsStyle="primary" bsSize="large">Not Succeed</button>
</div>
)
return (
<div onClick={this.handleClick.bind(this)}>
{this.state.succeed ? succeed : notsucceed}
</div>
)
}
};
You would call your api in there and set the state depending if the call fails or succeeeds. For this your api call must return a Promise. Something like:
handleClick() {
Api.call()
.then((response) => { this.setState({ succeed: true }); })
.catch((error) => { this.setState({ succeed: false }); })
}

Resources