Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Is there anyway to go through each component(Ex: button/label) characteristics using an infinite loop? In my app I have 24 buttons in a particular screen and I want to change color of each button one by one all the time. I want to change the color of each button one by one all the time. I have tried both componentdidmount and componentwillmount, but it happens once. When I go to another screen and come back, the loop doesnt start.
If you want to do this on a timed interval, you'd keep track of the selected item in your state, e.g.:
// In your constructor (since you mentioned `componentDidMount`, I know you're using classes)
this.state = {
selectedControl: 0,
// ...your other state
};
In componentDidMount, start your interval timer:
componentDidMount() {
this.timerHandle = setInterval(() => {
this.setState(({selectedControl, controls}) =>
({selectedControl: (selectedControl + 1) % controls.length})
);
}, 2000); // 2000ms = two seconds
}
When rendering the controls, highlight the selected one:
render() {
const {selectedControl, controls} = this.state;
return (
<div>
{controls.map((control, index) => (
<input key={index} type="button" value={control} className={index === selectedControl ? "highlighted" : undefined} />
))}
</div>
);
}
Note that in all of that I've assumed this.state.controls is an array of your controls.
Adjust as necessary, that's just to get you headed the right way.
Live Example (going a bit faster than 2 seconds):
class Example extends React.Component {
constructor(props) {
super(props);
// In your constructor (since you mentioned `componentDidMount`, I know you're using classes)
this.state = {
selectedControl: 0,
controls: ["one", "two", "three", "four"]
};
}
componentDidMount() {
this.timerHandle = setInterval(() => {
this.setState(({selectedControl, controls}) =>
({selectedControl: (selectedControl + 1) % controls.length})
);
}, 800); // 800ms = 0.8 seconds
}
render() {
const {selectedControl, controls} = this.state;
return (
<div>
{controls.map((control, index) => (
<input key={index} type="button" value={control} className={index === selectedControl ? "highlighted" : undefined} />
))}
</div>
);
}
}
ReactDOM.render(<Example />, document.getElementById("root"));
.highlighted {
font-weight: bold;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.development.js"></script>
Related
I'm building a pomodoro timer with 4 sessions. When a 25-minute timer is up, 1 is added to a state variable sessionNumber.
I have four of these circle/unchecked checkboxes displayed when the pomodoro cycle starts:
<FontAwesomeIcon
icon={faCircle}
size="2x"
className="pomodoro-unchecked session-checkbox"
/>
Each time 1 is added to sessionNumber state, I would like to hide one and display a different icon component:
<FontAwesomeIcon
icon={faCheckCircle}
size="2x"
className="pomodoro-checked session-checkbox"
/>
The only ways I could think of doing this would take a whole lot of code -- for example if statements based on each session number, like with an if statement, if the session is 0, display 4 unchecked circles (with the code for all four components), and if the session is 1, display 1 checked circle and 3 unchecked circles, and so forth. The second way I considered would be to give each one a different class name and in the method that changes the session number, display and hide each one, based on which session number it is (that would be more complicated). Is there a simpler, more succinct method?
You could use an array in state. You could initialise the array with four "faCircle" and then use the array.fill() method on setState to fill up the icons with "faCheckCircle". Then you can map over the array to render the appropriate icon.
class SessionIcons extends React.Component {
constructor(props) {
super(props);
this.state = {
sessions: 0,
icons: ["faCircle", "faCircle", "faCircle", "faCircle"]
};
}
onAddSession = () => {
this.setState(state => ({
sessions: state.sessions + 1,
icons: state.icons.fill("faCheckCircle", 0, state.sessions + 1)
}));
};
render() {
return (
<div>
{this.state.icons.map((icon, index) => (
<FontAwesomeIcon
key={icon + index}
icon={icon === "faCircle" ? faCircle : faCheckCircle}
size="2x"
/>
))}
<button onClick={this.onAddSession}>Add session</button>
</div>
);
}
}
Demo: https://codesandbox.io/s/shy-tdd-qzs1n
class Timer extends React.Component{
state = { sessionNumber: 1}
checkTimer = () =>{
..your logic to check timer every 25 mins
this.setState({timerState:1})
}
render(){
Let font;
if( this.state.sessionNumber == 1)
{
font = <FontAwesomeIcon
icon={faCircle}
size="2x"
className="pomodoro-unchecked session-checkbox"
/>
}
else if(this.state.sessionNumber == 2)
{
font = FontAwesomeIcon
icon={faCheckCircle}
size="2x"
className="pomodoro-checked session-checkbox"
/>
return(
{font}
)
}
}
I am quite new to programming and am currently working with React.js and API.
I am trying to get this quiz API https://opentdb.com/api.php?amount=10&category=20&difficulty=medium to respond in my app.
So far I have fetched all the information that I need but on click I want the app to show whether I have clicked on correct_answer from json or incorrect_answer and have it show a textbox with information. After 10 clicks it should stop counting and show the results and this is where I'm stuck and would appreciate a bit of help.
This is what I got so far:
import React, { Component } from "react";
import "./App.css";
const API =
"https://opentdb.com/api.php?amount=10&category=20&difficulty=medium";
class App extends Component {
constructor(props) {
super(props);
this.state = {
results: [],
score: 0,
correct_answer: "",
incorrect_answers: ""
};
}
handleClick = event => {
this.setState({
score: this.state.score + 1,
correct_answer: event.target.value,
incorrect_answers: event.target.value
});
};
componentDidMount() {
this.populateAppWithData();
}
populateAppWithData() {
const showData = fetch(API)
.then(response => response.json())
.then(data => this.setState({ results: data.results }));
console.log(showData);
}
render() {
var {} = this.state ? "You are correct" : "You are incorrect";
console.log();
const results = this.state.results.slice().map((result, index) => (
<ul onClick={this.handleClick.bind(this)} key={"result" + index}>
<li>
<h2> {result.question}</h2>
{""}
<h5>{result.correct_answer}</h5>
</li>
{result.incorrect_answers.map(incAnswer => (
<li>
<h5>{incAnswer}</h5>
</li>
))}
</ul>
));
return (
<div className="App">
<h1>Quiz App</h1>
<div>{results[Math.floor(Math.random() * results.length)]}</div>
<div>Score: {this.state.score}</div>
</div>
);
}
}
export default App;
Let's just restart from scratch. As every question can be answered or unanswered, right or wrong, it has to have its own state. Therefore it has to be its own component. So let's build a Question component that takes a right and a few incorrect answers, and a question, and calls back if the question was answered:
class Question extends Component {
state = { answered: undefined, isRight: undefined };
answerClicked(answer) {
const { hasAnswered, correct_answer } = this.props;
return event => {
if(this.state.answered) return; // prevent answering twice
const isRight = correct_answer === answer;
hasAnswered(isRight); // propagate to parent
this.setState({ answered: answer, isRight });
};
}
render() {
const { question, correct_answer, incorrect_answers } = this.props;
const { answered, isRight } = this.state;
return (
<div>
{question}
{[...incorrect_answers, correct_answers]/*shuffle?*/.map(answer => <div onClick={this.answerClicked(answer)} > {answer} </div>)}
{answered && `You answered ${answered}`}
{answered && isRight && "Thats right! :)"}
{answered && !isRight && "That's wrong :/"}
</div>
);
}
}
So far so good. Now you can add:
<Question question="Whats the answer to life the universe and everything?" correct_answer="42" incorrect_answers={["15", "12"]} hasAnswered={right => alert(right ? "Right" : "Wrong")} />
Somewhere, and see a question :)
Next Step: We want to group multiple questions and add a Counter. For that we use another component, that builds up Questions and maintains a counter in it's state, how many questions were answered already, and how many of them were answered right.
class Questions extends Component {
state = { right: 0, counter: 0 };
questionAnswered(isRight) {
this.setState(({ counter, right }) => ({ counter: counter + 1, right: right + isRight }));
}
render() {
const { questions } = this.props;
const { counter, right } = this.state;
const unanswered = questions.length - counter;
if(unanswered <= 0) {
return `All answered!`;
}
return (
<div>
You have {unanswered} questions left, {right} are rigjt already!
{ questions.map(it => <Question key={it.question} {...it} hasAnswered={it => this.questionAnswered(it)} />) }
</div>
);
}
}
Now again we can test this part of the application easily:
<Questions questions={[{ question: "What is the answer to life, the universe and everything?", correct_answer: "42", incorrect_answers: ["52", "12"] }, /*...*/]} />
Now the only thing that is left is to let App load the questions and use the <Questions /> component when the questions are available.
Through this split up, we do have a seperation of concerns:
The <Question> class manages anything related to asking the question and giving a "per question feedback"
The <Questions> component manages multiple questions and gives the overall feedback if all questions were answered.
The <App> component loads the questions.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I am new to ReactJS, Currently I am using ant.design for my interface and In ant.design I am using Switch Steps for Wizard form. I want to change Next Button style when clicked . I am new to this platform please guide me
Thanks
So to change the loading state you can modify the example in the docs to be:
import { Steps, Button, message } from 'antd';
const Step = Steps.Step;
const steps = [{
title: 'First',
content: 'First-content',
}, {
title: 'Second',
content: 'Second-content',
}, {
title: 'Last',
content: 'Last-content',
}];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
current: 0,
loading: false
};
}
next() {
const current = this.state.current + 1;
this.setState({ current, loading: true });
}
prev() {
const current = this.state.current - 1;
this.setState({ current });
}
render() {
const { current, loading } = this.state;
return (
<div>
<Steps current={current}>
{steps.map(item => <Step key={item.title} title={item.title} />)}
</Steps>
<div className="steps-content">{steps[current].content}</div>
<div className="steps-action">
{
current < steps.length - 1
&& <Button type="primary" loading={loading} onClick={() => this.next()}>Next</Button>
}
{
current === steps.length - 1
&& <Button type="primary" loading={loading} onClick={() => message.success('Processing complete!')}>Done</Button>
}
{
current > 0
&& (
<Button loading={loading} style={{ marginLeft: 8 }} onClick={() => this.prev()}>
Previous
</Button>
)
}
</div>
</div>
);
}
}
This will cause the button to go into loading state when Next is clicked. However you need some external event that is fed into the component to tell it when to remove the loading state.
This can be done by having a this.prop that is changed from the parent component and detected in componentWillReceiveProps, where you do this.setState({loading:false}), or by making next() call some asynch method which resets the loading state in its callback.
Original Question
I'm trying to render a list of items using React. The key is that the items share a common state, which can be controlled by each item.
For the sake of simplicity, let's say we have an array of strings. We have a List component that maps over the array, and generates the Item components. Each Item has a button that when clicked, it changes the state of all the items in the list (I've included a code snippet to convey what I'm trying to do).
I'm storing the state at the List component, and passing down its value to each Item child via props. The issue I'm encountering is that the button click (within Item) is not changing the UI state at all. I believe the issue has to do with the fact that items is not changing upon clicking the button (rightfully so), so React doesn't re-render the list (I would have expected some kind of UI update given the fact that the prop isEditing passed onto Item changes when the List state changes).
How can I have React handle this scenario?
Note: there seems to be a script error when clicking the Edit button in the code snippet, but I don't run into it when I run it locally. Instead, no errors are thrown, but nothing in the UI gets updated either. When I debug it, I can see that the state change in List is not propagated to its children.
Edited Question
Given the original question was not clear enough, I'm rephrasing it below.
Goal
I want to render a list of items in React. Each item should show a word, and an Edit button. The user should only be able edit one item at a time.
Acceptance Criteria
Upon loading, the user sees a list of words with an Edit button next to each.
When clicking Edit for item 1, only item 1 becomes editable and the Edit button becomes a Save button. The rest of the items on the list should no longer show their corresponding Edit button.
Upon clicking Save for item 0, the new value is shown for that item. All the Edit buttons (for the rest of the items) should become visible again.
Problem
On my original implementation, I was storing an edit state in the parent component (List), but this state wasn't properly being propagated to its Item children.
NOTE: My original implementation is lacking on the state management logic, which I found out later was the main culprit (see my response below). It also has a bind bug as noted by #Zhang below. I'm leaving it here for future reference, although it's not really a good example.
Here's my original implementation:
const items = ['foo', 'bar'];
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
isEditing: false
};
}
toggleIsEditing() {
this.setState((prevState) => {
return {
isEditing: !prevState.isEditing
}
});
}
render() {
return (
<ul>
{items.map((val) => (
<Item value={val}
toggleIsEditing={this.toggleIsEditing}
isEditing={this.state.isEditing}/>
))}
</ul>
);
}
}
class Item extends React.Component {
render() {
return (
<li>
<div>
<span>{this.props.value}</span>
{ !this.props.isEditing &&
(<button onClick={this.props.toggleIsEditing}>
Edit
</button>)
}
{ this.props.isEditing &&
(<div>
<span>...Editing</span>
<button onClick={this.props.toggleIsEditing}>
Stop
</button>
</div>)
}
</div>
</li>
);
}
}
ReactDOM.render(<List />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<body>
<div id="app" />
</body>
you didn't bind the parent scope when passing toggleIsEditing to child component
<Item value={val}
toggleIsEditing={this.toggleIsEditing.bind(this)}
isEditing={this.state.isEditing}/>
I figured out the solution when I rephrased my question, by rethinking through my implementation. I had a few issues with my original implementation:
The this in the non-lifecycle methods in the List class were not bound to the class scope (as noted by #ZhangBruce in his answer).
The state management logic in List was lacking other properties to be able to handle the use case.
Also, I believe adding state to the Item component itself was important to properly propagate the updates. Specifically, adding state.val was key (from what I understand). There may be other ways (possibly simpler), in which case I'd be curious to know, but in the meantime here's my solution:
const items = ['foo', 'bar'];
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
editingFieldIndex: -1
};
}
setEdit = (index = -1) => {
this.setState({
editingFieldIndex: index
});
}
render() {
return (
<ul>
{items.map((val, index) => (
<Item val={val}
index={index}
setEdit={this.setEdit}
editingFieldIndex={this.state.editingFieldIndex} />
))}
</ul>
);
}
}
class Item extends React.Component {
constructor(props) {
super(props);
this.state = {
val: props.val
};
}
save = (evt) => {
this.setState({
val: evt.target.value
});
}
render() {
const { index, setEdit, editingFieldIndex } = this.props;
const { val } = this.state;
const shouldShowEditableValue = editingFieldIndex === index;
const shouldShowSaveAction = editingFieldIndex === index;
const shouldHideActions =
editingFieldIndex !== -1 && editingFieldIndex !== index;
const editableValue = (
<input value={val} onChange={(evt) => this.save(evt)}/>
)
const readOnlyValue = (
<span>{val}</span>
)
const editAction = (
<button onClick={() => setEdit(index)}>
Edit
</button>
)
const saveAction = (
<button onClick={() => setEdit()}>
Save
</button>
)
return (
<li>
<div>
{ console.log(`index=${index}`) }
{ console.log(`editingFieldIndex=${editingFieldIndex}`) }
{ console.log(`shouldHideActions=${shouldHideActions}`) }
{
shouldShowEditableValue
? editableValue
: readOnlyValue
}
{
!shouldHideActions
? shouldShowSaveAction
? saveAction
: editAction
: ""
}
</div>
</li>
);
}
}
ReactDOM.render(<List />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<body>
<div id="app" />
</body>
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I want a border around the picture when I select it. So if I have 6 pictures and choose 3, I would like to have highlighted borders around those images. Question is,How can I do this? EDIT: I am using React for this dilemma.
This depends on how you want to track state in your app.. here is an example which tracks the state in the parent component. Essentially you have a single parent App component which tracks the state for each image, and then an Image component which renders either with or without a border, depending on a piece of the App state which gets passed down as a prop. Refer to the code to see what I mean. An alternative would be to have the active state live within each Image component itself.
The code has a number of interesting features mainly due to leveraging several aspects of ES6 to be more concise, as well as React's immutability helper to help update the state array in an immutable way, and lodash's times method to assist in creating our initial state array.
Code (some of the indenting got a bit muddled in the copy from jsfiddle..):
function getImage() {
return { active: false };
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { images: _.times(6, getImage) };
this.clickImage = this.clickImage.bind(this);
}
clickImage(ix) {
const images = React.addons.update(this.state.images, {
[ix]: { $apply: x => ({ active: !x.active}) }
})
this.setState({ images });
}
render() {
const { images } = this.state;
return (
<div>
{ images.map((image, ix) =>
<Image
key={ix}
ix={ix}
active={image.active}
clickImage={this.clickImage} />) }
</div>
);
}
};
class Image extends React.Component {
render() {
const { ix, clickImage, active } = this.props;
const style = active ? { border: '1px solid #021a40' } : {};
return <img
style={style}
src={`https://robohash.org/${ix}`}
onClick={() => clickImage(ix)}/>;
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
And then what it looks like:
Just add an event handler for click that adds a "selected" class, then set that selected class to have a border in css.
.selectableImg {
border: solid 1px transparent;
margin: 10px;
}
.selectableImg.selected {
border: solid 1px blue;
}
<img class="selectableImg" src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"/>
<img class="selectableImg" src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"/>
<img class="selectableImg" src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"/>
<img class="selectableImg" src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"/>
<script>
var images = document.querySelectorAll(".selectableImg");
images.forEach(function(i) {i.addEventListener("click", function(event) {
i.classList.toggle("selected");
})});
</script>