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.
Related
I'm having an Ant Design <Form> component with <Form.Items> which have onChange events. If the onChange event function is true I'm displaying extra content.
So in the example sandbox I created, when changing all the the <Radio> to Yes it fires the onChange event which is validated and then showing a div with the text "You checked all answered with yes".
As I'm using <Form> it is a form controlled environment so I'm using form to set and reset values. But when calling form.resetFields() the onChange handlers are not called. So the message won't go away as the state not refreshes. So I have to find a way to call a function from the parent component which refreshes the form values in the child component.
Using useImperativeHandle() for every field to update on more complex forms to call functions from the parent seems way too complex for such a simple task. And adding custom events to communicate with parent components seem to be a not very react way when reading this stack overflow thread
Is there something from the Ant Design form I'm missing? Because this must be a common task. What's a good way to approach this problem?
Link to code sandbox with an example:
https://codesandbox.io/s/vigilant-curran-dqvlc?file=/src/AntDFormChild.js
Example
const formLayout = {
labelCol: { span: 8 },
wrapperCol: { span: 7 }
};
const questionDefaultValues = {
rjr01_q01: 2,
rjr02_q01: 2
};
const AntDForm = () => {
const [form] = Form.useForm();
const handleResetForm = () => {
form.resetFields();
// now force onChange of child component to update
};
const handleFillForm = () => {
form.setFieldsValue({ rjr01_q01: 1, rjr02_q01: 1 });
// now force onChange of child component to update
};
return (
<>
<Button onClick={handleResetForm}>Reset Form</Button>
<Button onClick={handleFillForm}>Fill Form</Button>
<Form
{...formLayout}
form={form}
initialValues={{ ...questionDefaultValues }}
>
<AntDFormChild form={form} />
</Form>
</>
);
};
const questionQualifiedValues = {
rjr01_q01: 1,
rjr02_q01: 1
};
const AntDFormChild = ({ form }) => {
const [isQualified, setIsQualified] = useState(false);
const [questionFormValues, setQuestionFormValues] = useState({});
useEffect(() => {
if (shallowEqual(questionFormValues, questionQualifiedValues)) {
setIsQualified(true);
} else {
setIsQualified(false);
}
}, [questionFormValues]);
function shallowEqual(object1, object2) {
const keys1 = Object.keys(object1);
const keys2 = Object.keys(object2);
if (keys1.length !== keys2.length) {
return false;
}
for (let key of keys1) {
if (object1[key] !== object2[key]) {
return false;
}
}
return true;
}
return (
<>
{isQualified && (
<div style={{ color: "red" }}>You checked all answered with yes</div>
)}
<Form.Item name="rjr01_q01" label="Question 1">
<Radio.Group
onChange={(i) => {
setQuestionFormValues((questionFormValues) => ({
...questionFormValues,
rjr01_q01: i.target.value
}));
}}
>
<Radio value={1}>Yes</Radio>
<Radio value={0}>No</Radio>
<Radio value={2}>Unknown</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name="rjr02_q01" label="Question 2">
<Radio.Group
onChange={(i) => {
setQuestionFormValues((questionFormValues) => ({
...questionFormValues,
rjr02_q01: i.target.value
}));
}}
>
<Radio value={1}>Yes</Radio>
<Radio value={0}>No</Radio>
<Radio value={2}>Unknown</Radio>
</Radio.Group>
</Form.Item>
</>
);
};
Since AntD Form is uncontrolled, there is no way to trigger onChange event by calling resetFields, setFieldsValues.
I think your goal is to show the message depending on form values, and the best way to do is to use Form.Item, which can access form state.
https://codesandbox.io/s/antd-form-item-based-on-other-item-ens59?file=/src/AntDFormChild.js
I have two user settings options. One is "enable cascading panels", and the other is "include attachments".
I have set up state variables and toggle functions to pass down to the child components so that when I click on either, the parent is able to toggle between the values. The issue is that when I click on either, the other one executes as well. Did I miss a step in setting these up?
Parent component data:
State:
constructor(props){
const userToggleSettings = {
cascadingPanels: true,
includeAttachments: true,
analyticsOptIn: false
};
this.state = {
userToggleSettings
};
}
Toggle functions:
toggleIncludeAttachments = () => {
this.setState((prevState) => (
{
userToggleSettings:
{
includeAttachments: !prevState.userToggleSettings.includeAttachments
}
}
));
};
toggleCascadingPanels = () => {
this.setState((prevState) => (
{
userToggleSettings:
{
cascadingPanels: !prevState.userToggleSettings.cascadingPanels
}
}
));
};
includeAttachmentsClickHandler = () => {
this.toggleIncludeAttachments();
}
cascadingPanelsClickHandler = () => {
this.toggleCascadingPanels();
}
Passing values and functions as props:
<ChildComponent
attachmentsSwitchHandler={this.toggleIncludeAttachments}
attachmentsSwitchValue={this.state.userToggleSettings.includeAttachments}
cascadingPanelsSwitchHandler={this.toggleCascadingPanels}
cascadingPanelsSwitchValue={this.state.userToggleSettings.cascadingPanels}
/>
Child component data
Setting up click events and values in child component:
<div className='child-component-container'>
<div className={'user-settings-toggle'}
onClick={props.cascadingPanelsSwitchHandler}
>
<div className={'user-settings-label'}>
{props.translations.CASCADING_PANELS}
</div>
<Switch
checked={props.cascadingPanelsSwitchValue}
translations={{
off: props.translations.off,
on: props.translations.ON
}}
/>
</div>
<div className={'user-settings-toggle'}
onClick={props.attachmentsSwitchHandler}
>
<Switch
checked={props.attachmentsSwitchValue}
translations={{
off: props.translations.off,
on: props.translations.ON
}}
/>
<div className={'user-settings-label'}>
{props.translations.ALWAYS_INCLUDE_ATTACHMENTS}
</div>
</div>
Demo:
Could someone clarify what I am doing wrong here?
You should change your toggle functions in such way:
this.setState((prevState) => (
{
userToggleSettings:
{
...prevState.userToggleSettings, // ---> added
cascadingPanels: !prevState.userToggleSettings.cascadingPanels
}
}
));
React doesn't perform a "deep merge", i.e. the nested objects are not merged. Only top level properties are merged.
So the way you had it you were losing whatever was there inside the userToggleSettings object, after doing setState.
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>
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.
I am trying to remove a div when onClick is pressed. The div exists on my parent component where I have
render() {
const listPlayers = players.map(player => (
<Counter
key={player.id}
player={player}
name={player.name}
sortableGroupDecorator={this.sortableGroupDecorator}
decrementCountTotal={this.decrementCountTotal}
incrementCountTotal={this.incrementCountTotal}
removePlayer={this.removePlayer}
handleClick={player}
/>
));
return (
<ContainLeft style={{ alignItems: 'center' }}>
<ProjectTitle>Score Keeper</ProjectTitle>
<Copy>
A sortable list of players that with adjustable scores. Warning, don't go negative!
</Copy>
<div>
<Stats totalScore={this.state.totalScore} players={players} />
{listPlayers}
</div>
</ContainLeft>
);
}
It passes props to the child component where the button to delete the div, here
return (
<div
style={{ display: this.state.displayInfo }}
className="group-list"
ref={sortableGroupDecorator}
id="cell"
>
<CountCell style={{ background: this.state.color }}>
<Row style={{ alignItems: 'center', marginLeft: '-42px' }}>
<Col>
<DeleteButton onClick={removePlayer}>
<Icon name="delete" className="delete-adjust fa-minus-circle" />
</DeleteButton>
</Col>
(I snipped the rest of the code because it was long and not useful here)
The array (a separate file) is imported into the Parent component and it reads like this
const players = [
{
name: 'Jabba',
score: 10,
id: 11
},
{
name: 'Han',
score: 10,
id: 1
},
{
name: 'Rey',
score: 30,
id: 10
}
];
export default players;
So what I'm trying to do is write a function on the main parent that when it is clicked inside the child, the div is removed, deleted, gone (whatever the best term is) sort of like "remove player, add player"
On my parent component, I've written a function where the console.log works when it is clicked in the child, but whatever I write in the function doesn't seem to want to work.
The function I'm building (in progress, I'm still a little lost here) is:
removePlayer() {
console.log('this was removed');
players.splice(2, 0, 'Luke', 'Vader');
}
which is mapped over here as a prop
const listPlayers = players.map(player => (
<Counter
key={player.id}
player={player}
name={player.name}
sortableGroupDecorator={this.sortableGroupDecorator}
decrementCountTotal={this.decrementCountTotal}
incrementCountTotal={this.incrementCountTotal}
removePlayer={this.removePlayer}
handleClick={player}
/>
));
And passed into the child here:
render() {
const {
name,
sortableGroupDecorator,
decrementCountTotal,
incrementCountTotal,
removePlayer
} = this.props;
return (
<div
style={{ display: this.state.displayInfo }}
className="group-list"
ref={sortableGroupDecorator}
id="cell"
>
<CountCell style={{ background: this.state.color }}>
<Row style={{ alignItems: 'center', marginLeft: '-42px' }}>
<Col>
<DeleteButton onClick={removePlayer}>
<Icon name="delete" className="delete-adjust fa-minus-circle" />
</DeleteButton>
I know all this is lengthy and I wanted to provide as much detail as I could because React is still new to me and I get confused with some of the verbiages. Thanks for helping out in advance
We sorted it out in chat. Like expected, it was a problem with the state.
I made a small semi-pseudo snippet with comments as explanation:
import React, { Component } from 'react';
// Your player constant, outside the scope of any React component
// This pretty much just lives in your browser as a plain object.
const players = [
{
name: 'Jabba',
score: 10,
id: 11
},
{
name: 'Han',
score: 10,
id: 1
},
{
name: 'Rey',
score: 30,
id: 10
}
];
class App extends Component {
constructor() {
super();
this.state = {
players, // ES6 Syntax, same as players: players
// Add all your other stuff here
};
}
removePlayer(id) {
const newState = this.state;
const index = newState.players.findIndex(a => a.id === id);
if (index === -1) return;
newState.players.splice(index, 1);
this.setState(newState); // This will update the state and trigger a rerender of the components
}
render() {
const listPlayers = this.state.players.map(player => { // Note the this.state, this is important for React to see changes in the data and thus rerender the Component
<Counter
..
removePlayer={this.removePlayer.bind(this)} //bind this to stay in the context of the parent component
/>
});
return (
<div>
{listPlayers}
</div>
);
}
}
//////////////////////////////////////// Child component
....
<DeleteButton onClick={() => this.props.removePlayer(this.props.player.id)}>
....
Here is how i solved this
i've added an id to the element
and then with function remove i detroyed it
const closenotif = () => document.getElementById("notif").remove()
<div id="notif">
<button onClick={destroy}> close </button>
</div>
NB : the element is destroyed in the current document
so in the current render
on Next JS this works perfectly
if you are using a live rendering with react this probably won't work
i'll suggest you to work with states in that case
Little confused about how the whole app works, but I will try to help you.
To make react changes to the dom, you have to put players in the state. So, in the removePlayer you make a copy of this.state.players in a local variable (just to not change the array directly in state, it's a good practice), then you make the split in this local variable and finally you setState({ players: localPlayers}).
This way the "div" will be removed.