Component didn't re-renders on state change? - javascript

I am creating a random quote generator. There is a quote box that displays quote and author names. I created a method to invoke on a button click that may randomize the quote list and display a new quote and a next button to get next quote from my quote list. I can see the first quote but the component didn't re-renders on clicking buttons or something gets wrong that I can't get next quote or can't randomize. Here is the code:
class UI_qoutebox extends React.Component {
constructor(props) {
super(props);
this.state = { qoutes: props.qoutes, authors: props.authors, num: 0 };
this.UI_rq = this.UI_rq.bind(this);
this.UI_next = this.UI_next.bind(this);
}
UI_rq() {
let rnd = Math.floor(Math.random() * this.state.qoutes.length);
this.setState({ num: rnd });
}
UI_next() {
let p = this.state.num + 1;
if (p > this.state.qoutes.length) { p = 0 }
this.setState({ num: p })
}
render() {
const { qoutes, authors, num } = this.state;
return (
<div className="qoute-box">
<span className="qoute">{qoutes[num]}</span>
<span>{authors[num]}</span>
<input type="button" value="Randomize" onClick={() => this.UI_rq} />
<input type="button" value="Next" onClick={() => this.UI_next} />
</div>
)
}
}
I am working on Freecodecamp's project and I need quick help. Thanks in advance.

Change this
<input type="button" value="Randomize" onClick={this.UI_rq}/>
<input type="button" value="Next" onClick={this.UI_next}/>

Related

How to dynamically add and remove text input fields in Svelte correctly?

I am new to Svelte and web development and I hope you could point me in the right direction.
The current code belongs in a svelte component.
As shown here: https://svelte.dev/repl/f0e5c30117724ec38b7d19781d2c4de6?version=3.48.0
It is supposed to show one text field by default, while allowing for an additional text field to be added and removed on dynamically added buttons.
Currently, this code can dynamically add the text field, however, it cannot dynamically remove the text field on button click.
I believe there might be an error in the GetDynamicElement function. However, I am not sure where exactly. Any suggestions?
p.s. I know there are answers here that are close, but I don't think they are applicable in this situation, especially on Svelte.
<script>
var num_links = 1;
let container;
const GetDynamicElement = (value) => {
return (
'<input name = "DynamicField" type="text" size =111 id =link placeholder="Enter Next link! " value = "' +
value +
'" />' +
'<input type="button" value="Remove" on:click = {RemoveField(this)}>'
// "RemoveSuggestionCart(this)" />'
);
};
const addField = () => {
if (num_links < 2) {
console.log("addField");
const div = document.createElement("DIV");
div.innerHTML = GetDynamicElement("");
container.appendChild(div); // Append timetable space
num_links += 1;
}
};
//Removes the entire division inclusive of it's text field.
const RemoveField = (div) => {
console.log("RemoveField");
div.removeChild(div.parentNode);
num_links -= 1;
};
</script>
<div>
<input
name="DynamicField"
type="text"
size="121"
id="link"
placeholder="Enter First Link!"
/>
<div bind:this={container} />
</div>
<button on:click|preventDefault={addField}>[+ add timetable link]</button>
<style>
</style>
Add/ remove fields and have a button to log or send to endpoint or whatever with "Log" button.
<script>
// to display one empty inputs before clicking add needed
let values=[{
"url": "",
"name": "",
}];
const addField = () => {
values = [...values, {url: '', name: ''}]
};
const removeField = () => {
values = values.slice(0, values.length-1)
};
</script>
{#each values as v, i}
<div>
<input id={i} type="text" bind:value={values[i].url} placeholder="url"/>
<input id={i} type="text" bind:value={values[i].name} placeholder="name"/>
</div>
{/each}
{#if values.length >= 2}
<input type="button" value="Remove" on:click={removeField}>
{/if]
<button on:click|preventDefault={addField}>Add</button>
<button on:click={() => console.log(values)}>Log Me</button>
Try: https://svelte.dev/repl/2441993f8d9946aa894bf07a8a8f9b4f
Edited: thanks #Corrl - edited nicer.
You can use your num_link and svelte's #each to create to inputs using svelte:
{#each Array(num_links) as _, i}
<div>
<input
name="DynamicField"
type="text"
size="121"
id={`link_${i}`}
placeholder="Enter First Link!"
/>
</div>
{/each}
Working example: https://svelte.dev/repl/04169e030b6944258cfd07af15873b48?version=3.48.0

How do you pass the value inputted from a prompt to a function?

I am creating a hangman game using react and js. After pressing the Guess word button I want to use a prompt box to have the user enter what they think the full word is. However, when I input a word into the prompt box I get "TypeError: Object doesn't support property or method 'checkWholeWord'". I am new to js coding and I really don't know what I am doing wrong here.
import React, { Component } from 'react';
import './App.css';
import words from './words'
class App extends Component {
//chooses random word
state = {
word: words[Math.floor(Math.random() * words.length)],
guessedLetters: [],
guessesRemaining: 6
}
//pass function to child to update
updateGuessedLetters(letter){
if(this.state.guessedLetters.includes(letter)){
alert('ALREADY TRIED THAT')
}else{
this.setState({
//makes new array with letter
guessedLetters: [...this.state.guessedLetters, letter]
})
}
}
//keeps track of how many times wrong letter is entered
updateGuessesRemaining(letter){
if(!this.state.guessedLetters.includes(letter) && !this.state.word.split('').includes(letter)){
this.setState({guessesRemaining: this.state.guessesRemaining -1})
}
}
//fix this function
wordIsGuessed(){
const guessState = this.state.word.split('').map(letter => {
if (this.state.guessedLetters.includes(letter)){
return letter
}
})
return guessState.join('') === this.state.word
}
//checks if game is over by checking guesses remaining
gameOver(){
if(this.state.guessesRemaining <= 1){
alert('You lose')
}else if (this.wordIsGuessed()){
alert('You win!')
}
}
//when someone attempts to guess whole word checks inputted word
checkWholeWord(wholeWord){
if (wholeWord===this.state.word){
alert('You Win')
}
}
//called every time a letter is submitted, if gameover then end
updateGameState(letter){
this.gameOver()
this.updateGuessedLetters(letter)
this.updateGuessesRemaining(letter)
}
render() {
return (
<div className="App">
<h1>Hangman</h1>
{this.state.word}
<DisplayWord word={this.state.word} guessedLetters={this.state.guessedLetters}/>
<SubmitGuess updateGameState = { (letter) => this.updateGameState(letter)}/>
<WrongLetters word={this.state.word} guessedLetters={this.state.guessedLetters}/>
</div>
);
}
}
class DisplayWord extends Component{
render(){
//splits word into array
const wordLetters = this.props.word.split('')
const answer = wordLetters.map(letter => {
let letterState = '_' + ' '
if(this.props.guessedLetters.includes(letter)){
letterState=letter
}
return letterState
})
return(
<div className="DisplayWord">
<h3>{answer}</h3>
</div>
);
}
}
class WrongLetters extends Component {
getWrongLetters(){
//filter checks if letter is in word
const wrong = this.props.guessedLetters.filter(letter =>{
return !this.props.word.split('').includes(letter)
})
return wrong
}
render(){
return(
<div className="WrongLetters">
{this.getWrongLetters()}
</div>
);
}
}
//takes guess from user
class SubmitGuess extends Component {
//ensures that the page doesn't refresh completely
handleSubmit = (e) => {
e.preventDefault()
//gets value from user
const input = this.refs.textInput.value
this.props.updateGameState(input)
//resets value to empty
this.refs.textInput.value = ''
}
guessWholeWord = (e) => {
e.preventDefault()
const wholeWord = prompt ("Guess the word :)")
this.props.checkWholeWord(wholeWord)
}
render(){
return(
<div>
<form ref="form" onKeyUp={this.handleSubmit}>
<input type="text" ref='textInput' maxLength='1'/>
<button>New Game</button>
</form>
<form ref="forms" onSubmit={this.guessWholeWord}>
<button type ="submit">Guess Word</button>
</form>
</div>
);
}
}
export default App;
guessWholeWord = (e) => {
e.preventDefault()
const wholeWord = prompt ("Guess the word :)")
if (wholeWord===this.state.word){
alert('You win!')
}
}
render() {
return (
<div className="App">
<h1>Hangman</h1>
{this.state.word}
<DisplayWord word = {this.state.word} guessedLetters={this.state.guessedLetters}/>
<SubmitGuess updateGameState = { (letter) => this.updateGameState(letter)}/>
<WrongLetters word={this.state.word} guessedLetters={this.state.guessedLetters}/>
<form ref="forms" onSubmit={this.guessWholeWord}>
<button type ="submit">Guess Word</button>
</form>
</div>
);

React - Can't change DOM's Style with JavaScript

I want to dynamically change my DOM's height. But I can't. No errors, console.log prints nothing, just blank with a number indicating how many times this blank line is printed on console. ref works but it seems that whatever I write to the ref won't be updated to the DOM.
On each keypress, my resizeTextInput function is invoked.
constructor(props) {
super(props);
this.state = {
textLen: 0,
commentError: false,
};
this.textInputRef = React.createRef();
this.commentBlockRef = React.createRef();
this.resizeTextInput = this.resizeTextInput.bind(this);
this.handleSearch = this.postComment.bind(this);
this.postComment = this.postComment.bind(this);
}
resizeTextInput() {
this.commentBlockRef.current.style.height = 300;
console.log(this.commentBlockRef.current.style.height);
}
postComment(event) {
this.resizeTextInput();
}
render() {
return (
<div className="comment-wrap">
<div className={`comment-block ${className}`} ref={this.commentBlockRef}>
<textarea className={`textarea-${className}`} type="text" placeholder="Write a comment..." onKeyUp={this.postComment} ref={this.textInputRef} size="55" />
<div className={`comment-block-len ${(commentError || postError) ? 'comment-error' : ''}`}>{textLen}/{MAX_LEN}</div>
</div>
</div>
);
}
}
It should be 300px in string instead 300 only.
this.commentBlockRef.current.style.height = '300px';

set state in onClick function

I have 2 submit buttons in my react form.
I am trying to use an onClick function to get the id of the clicked button, so I can then specify how to handle each accordingly.
My onClick function is returning undefined for the setState of the id.
How can I properly grab the id of the button and set the state?
handleClick() {
var buttons = document.getElementsByTagName("button");
var buttonsCount = buttons.length;
for (var i = 0; i < buttonsCount; i++) {
buttons[i].onclick = (e) => {
this.setState({clickedSubmit: this.id});
console.log(this.state.clickedSubmit); //returns undefined
};
}
}
//in the render
<button id="formSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick}>Submit</button>
<button id="hashSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick}>Generate Hash</button>
You will find your element id in the event of the onClick handler that is e.target.id
handleClick = (e) => {
this.setState({ clickedSubmit: e.target.id },() => {
console.log(this.state.clickedSubmit)
});
}
//in the render
<button id="formSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick}>Submit</button>
<button id="hashSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick}>Generate Hash</button>
I recommend changing how you invoke handleClick slightly, by passing in an argument for buttonId.
In your render function:
<button onClick={() => this.handleClick('formSubmit')}>Submit</button>
In handleClick:
handleClick(buttonId) { ... }
As I posted in my comment, you have an option to separate the button out into it's own component. In that case, you would be able to use simple like this.props.id to get the value of id.
I would suggest you to go for a simpler approach, something like this
handleClick1() {
var buttons = document.getElementsByTagName("button");
var buttonsCount = buttons.length;
let id = 1;
for (var i = 0; i < buttonsCount; i++) {
buttons[i].onclick = (e) => {
this.setState({clickedSubmit: id});
console.log(this.state.clickedSubmit); //returns undefined
};
}
}
handleClick2() {
var buttons = document.getElementsByTagName("button");
var buttonsCount = buttons.length;
let id = 2;
for (var i = 0; i < buttonsCount; i++) {
buttons[i].onclick = (e) => {
this.setState({clickedSubmit: id});
console.log(this.state.clickedSubmit); //returns undefined
};
}
}
//in the render
<button id="formSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick1}>Submit</button>
<button id="hashSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick2}>Generate Hash</button>
The benefit of this approach is, in future when your button count increases, then at that time, your code should be modular so that it is easy to add new functionality to your app.
Hope this helps!
You can get id of your buttons by using event.target, like below:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
clickedId: -1,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
const id = event.target.id;
this.setState({
clickedId: id,
});
}
render() {
return (
<div>
<h1>Clicked id: {this.state.clickedId}</h1>
<button id="asd" onClick={this.handleClick}>Asd</button>
<button id="qwe" onClick={this.handleClick}>Qwe</button>
</div>
);
}
}
DEMO: http://jsbin.com/yiqavav/1/edit?html,js,output
You can try this, even change id to other parameter you want to pass in to the your function.
<button onClick={(e) => this.handlefunction(id, e)}>Delete Row</button>
<button onClick={this.handlefunction.bind(this, id)}>Delete Row</button>
This article helps a great deal:
https://reactjs.org/docs/handling-events.html

uncheck checkbox programmatically in reactjs

I am messing with checkboxes and I want to know that is there a way in which I can uncheck a checkbox on click of a button by calling a function?? If so? How can I do that?
<input type="checkbox" className="checkbox"/>
<button onClick={()=>this.unCheck()}
How can I uncheck the checkbox programmatically and what if I have multiple checkboxes generated dynamically using map function.
How can I uncheck them If I want to?
There is property of checkbox checked you can use that to toggle the status of check box.
Possible Ways:
1- You can use ref with check boxes, and onClick of button, by using ref you can unCheck the box.
2- You can use controlled element, means store the status of check box inside a state variable and update that when button clicked.
Check this example by using ref, assign a unique ref to each check box then pass the index of that item inside onClick function, use that index to toggle specific checkBox:
class App extends React.Component{
constructor(){
super();
this.state = {value: ''}
}
unCheck(i){
let ref = 'ref_' + i;
this.refs[ref].checked = !this.refs[ref].checked;
}
render(){
return (
<div>
{[1,2,3,4,5].map((item,i) => {
return (
<div>
<input type="checkbox" checked={true} ref={'ref_' + i}/>
<button onClick={()=>this.unCheck(i)}>Toggle</button>
</div>
)
})}
</div>
)
}
}
ReactDOM.render(<App/>, 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>
<div id='app'/>
Check this example of controlled element, means storing the state of checkbox inside state variable, and on click of button change the value of that:
class App extends React.Component{
constructor(){
super();
this.state = {value: []}
}
onChange(e, i){
let value = this.state.value.slice();
value[i] = e.target.checked;
this.setState({value})
}
unCheck(i){
let value = this.state.value.slice();
value[i] = !value[i];
this.setState({value})
}
render(){
return (
<div>
{[1,2,3,4,5].map((item,i) => {
return (
<div>
<input checked={this.state.value[i]} type="checkbox" onChange={(e) => this.onChange(e, i)}/>
<button onClick={()=>this.unCheck(i)}>Toggle</button>
</div>
)
})}
</div>
)
}
}
ReactDOM.render(<App/>, 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>
<div id='app'/>
React
Checked
Using State
<input type="radio" name="count" value="minus" onChange={this.handleRadioChange} checked={this.state.operation == "minus"} /> Decrement
2.Using Refs
<input type="radio" name="count" ref="minus" /> Decrement
onSubmit(e){ this.refs.minus.checked = false }
Using plain javascript you can acheive like below.
function unCheck() {
var x = document.getElementsByClassName("checkbox");
for(i=0; i<=x.length; i++) {
x[i].checked = false;
}
}
Small DEMO
I was thinking of a thing like that:
<input onChange={(input) => this.onFilterChange(input)} className="form-check-input" type="checkbox" />
onFilterChange = (input) => { let { value, checked } = input.target;}
unCkeckAll = () => {
[...document.querySelectorAll('.form-check-input')].map((input) => {
if (input.checked) {
let fakeInput = {
target: {
value: input.value,
checked: false
}
}
input.checked = !input.checked;
this.onFilterChange(fakeInput);
}
return null;
})
}
Checkboxes have a checked property, you can hook it to the state and change it dynamically. Check these links:
https://facebook.github.io/react/docs/forms.html#handling-multiple-inputs
http://react.tips/checkboxes-in-react/
Sometime its good to use plain javascript. If you have of checkbox value in any of your state then try this
let checkboxValue = xyz
document.querySelectorAll("input[value="+checkboxValue+"]")[0].checked = false;

Categories

Resources