Error passing a selected value to a function - reactjs

I have the following component that shows the options of a quiz, when the user select a value I want to pass the "item.id" selected in the "handleNext", no código abaixo aparece o seguinte erro:
Uncaught ReferenceError: item is not defined
code:
render () {
return (
<UserContext.Provider value={this.state}>
<UserContext.Consumer>
{({ current_statement, current_alternative, handleNext}) => (
<form>
<p>{current_statement}</p>
{this.state.current_alternative.map(item => (
<React.Fragment key={item.id}>
<div>
<input id={item.id} type="radio" name="question"/>
<label htmlFor={item.id}>{item.content}</label>
</div>
</React.Fragment>
))}
<button onClick={(e) => this.handleNext(e, item.id)} type="button">Next</button>
</form>
)}
</UserContext.Consumer>
</UserContext.Provider>
)
}

handleRadioChange = id => this.setState({selected: id})
render() {
...
<input id={item.id} type="radio" name="question" onChange={e => this.handleRadioChange(e.event.target.id)} />
...
<button onClick={(e) => this.handleNext(e, this.state.selected)} type="button">Next</button>
In short:
You have to store selected id of radio button selected by user in state, then your button will take id from state when the user presses it.

The problem is your binding of your Next button:
this.handleNext(e, item.id)
You've added this OUTSIDE of your .map() function, so there is no item (This is explicitly defined on each loop of your this.state.current_alternative map). Move this inside of the .map() array, and you'll be able to access item.

Related

Why My Checkboxes Doesn't Check Back After Uncheck in ReactJS

I have checkboxes that you can click as many as you want.
My problem is that it doesn't put a check after I uncheck. Also the values when I submit is not appearing in console.log
Pls check my codesandbox here
CLICK HERE
<Label htmlFor={id}>
<Input
type="checkbox"
id={name}
name={name}
value={value}
checked={checked}
onChange={onChange}
/>
<Indicator />
</Label>
<form onSubmit={handleSubmit}>
{products.map((product) => (
<div key={product}>
<Input
name={values.products}
value={values.products}
checked={values.products}
onChange={({ target }) => setFieldValue("products", target.value)}
/>
</div>
))}
<button type="submit">Submit</button>
</form>
when you are checking a checkbox your will get the value and check whether if the value is present already in the products list if it is not present then you should add the value ( this will be your check part ) else you can filter the value from the products ( this will be your uncheck ) .
<Checkbox
name="products"
value={product}
checked={values.products.includes(product)}
onChange={({ target }) => {
let updatedProducts;
if (values.products.includes(product)) {
updatedProducts = values.products.filter(
(product) => product !== target.value
);
} else {
updatedProducts = [...values.products, target.value];
}
setFieldValue("products", updatedProducts);
}}
/>
Working Sandbox
Sandbox
values.products is an array of your values, so it doesn't make sense to have this for your name or value. Instead, you need to pick out the specific value from the array that is relevant to your specific checkbox.
{products.map((product, index) => (
<div key={product}>
<p>{product}</p>
<Input
name={product}
value={values.products[index]}
checked={values.products[index]}
onChange={({ target }) => setFieldValue(`products.${index}`,!values.products[index])
}
/>
</div>
))}
Working Sandbox

React component second time render issue

I am building a tasks board app and I have an issue with rendering the TasksList component within a board of 3 lists: 1 Board -> 3 lists -> N tasks
It seems like the TasksList component is being rendered twice, which is fine, but on the 2nd time it seems to return different values for each task (which are wrong according to my conditional return, and right on the first render - why would there be a difference?)
I also get this warning. Maybe ts related:
Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
Board.js component render function:
const tasksListsArr = Object.entries(this.state.tasks).map(list => {
return (
<TasksList
key={list[0]}
listrole={list[0]}
listTitle={this.state.Lists[list[0]]}
tasks={list[1]}
listTextChangedHandler={event => this.listTextChangedHandler(list[0], event)}
addTaskHandler={() => this.addTaskHandler(list[0])}
taskDeleteHandler={this.taskDeleteHandler}
moveTaskHandler={this.moveTaskHandler}
taskEditHandler={this.taskEditHandler}
taskEditApprove={this.taskEditApprove}
/>
)
})
TaskList.js component:
import React from "react"
import classes from "./TasksList.module.css"
const TasksList = props => {
const tasks = props.tasks.map(task => {
const buttonLeft =
!task.isEdit && (props.listrole === "inprogress" || props.listrole === "done") ? (
<button onClick={() => props.moveTaskHandler(task.id, "left")}>left</button>
) : null
const buttonRight =
!task.isEdit && (props.listrole === "inprogress" || props.listrole === "backlog") ? (
<button onClick={() => props.moveTaskHandler(task.id, "right")}>right</button>
) : null
const taskUtils = task.isEdit ? null : (
<div>
<span onClick={() => props.taskDeleteHandler(props.listrole, task.id)}>X</span>
<span onClick={() => props.taskEditHandler(props.listrole, task.id)}>edit</span>
</div>
)
const taskContent = task.isEdit ? (
<div>
<input
type='text'
onChange={event => props.listTextChangedHandler(props.listrole, event)}
/>
<button onClick={props.taskEditApprove(props.listrole, task.id)}>OK</button>
</div>
) : (
<div>
<div>{task.text}</div>
</div>
)
return (
<div key={task.id} className={classes.Task}>
{buttonLeft}
{taskContent}
{buttonRight}
{taskUtils}
</div>
)
})
console.log(tasks)
return (
<div className={classes.List}>
<h2 className={classes.ListTitle}> {props.listTitle} </h2>
<input type='text' onChange={props.listTextChangedHandler} placeholder='Add task...' />
<button onClick={props.addTaskHandler}>+</button>
<div className={classes.TasksList}>{tasks}</div>
</div>
)
}
export default TasksList
I suspect the issue is in TaskList component. Because the onChange of input and onClick of button getting called on every render but those event handler functions should be called only when user integrated with it. So to fix change them to arrow way so that the function gets called only when we interact.
Following changes required in TaskList.js
Change
<button onClick={props.taskEditApprove(props.listrole, task.id)}>OK</button>
To
<button onClick={() => props.taskEditApprove(props.listrole, task.id)}>OK</button>
And
Change
<input type='text' onChange={props.listTextChangedHandler} placeholder='Add task...' />
<button onClick={props.addTaskHandler}>+. </button>
To
<input type='text' onChange={event => props.listTextChangedHandler(event)} placeholder='Add task...' />
<button onClick={() => props.addTaskHandler()}>+</button>
OK
props.taskEditApprove is being called in render.
Try
props.taskEditApprove(props.listrole, task.id)}>OK
Then function will be called only on interaction.

React/Mobx: Adding checkbox handler breaks a textarea field

I recently added a few checkboxes to a component. In the map function below, 5 of them are rendered.
After attaching a checkbox onchange handler, not only did the check/uncheck action fail to implement, but editing the text of a textarea in this component now breaks the editing function and causes an infinite loop in MobX (in the console).
However, removing the onChange handler from the checkboxes immediately fixes the issue with the textarea.
I don't see how the 2 are related, does anyone know what is happening? Here is the component:
const FinalEditsAndCopy = (props) => {
const update_final_textarea = (text_input) => {
ui_store.set_final_text_message(text_input.target.value);
console.log(text_input.target.value);
};
const render_social_media_checkboxes = () => {
return static_local_data.social_media_sites.map((social_media_site, i) => (
<List.Item key={i}>
<List.Content>
<input
type="checkbox"
checked={ui_store.final_social_media_site_selections[social_media_site]}
name={social_media_site}
className={ checkbox_style }
onChange={ ui_store.reverse_checkbox_state(social_media_site) } // This is the line that breaks the textarea and also does not fill its intended purpose
/>
{Lodash.capitalize(social_media_site)}
</List.Content>
</List.Item>
))
};
const render_social_media_checkboxes_test = () => {
return static_local_data.social_media_sites.map((social_media_site, i) => (
<List.Item key={ i }>
<List.Content>
<p>Test</p>
</List.Content>
</List.Item>
));
};
return (
<div className={ outer_container_style }>
<Container>
<Form>
<TextArea autoHeight
value={ ui_store.final_text_message }
className={ textarea_style }
onChange={ update_final_textarea }
/>
<Message attached='bottom' positive className={ alert_style }>
<Icon
name='clipboard list'
color='green'
size='big'
/>
Copied to clipboard
</Message>
</Form>
<Header size='small'>Select Social Media Sites</Header>
<div>
<List horizontal>
{render_social_media_checkboxes()}
</List>
</div>
<Button size='huge' color='orange'>Copy Text and Open Social Media Sites in New Tabs</Button>
</Container>
</div>
);
};
export default observer(FinalEditsAndCopy);
MobX observables in ui_store:
final_text_message = '';
final_social_media_site_selections = {
"facebook" : true,
"twitter" : true,
"linkedin" : true,
"instagram" : true,
"pinterest" : true
};
And the relevant actions:
set_final_text_message(input_message) {
this.final_text_message = input_message
}
reverse_checkbox_state(social_media_site) {
this.final_social_media_site_selections[social_media_site] = !this.final_social_media_site_selections[social_media_site]
}
You are invoking reverse_checkbox_state directly on render. You want to give onChange a function that will be called when the change event occurs, not invoke the function yourself.
<input
type="checkbox"
checked={ui_store.final_social_media_site_selections[social_media_site]}
name={social_media_site}
className={ checkbox_style }
onChange={() => ui_store.reverse_checkbox_state(social_media_site)}
/>

React having onClick work only once on a component

Hello I am trying to build a forum site. I have simple categories and comments. I have a button that displays a text box to enter a comment on a category, but when I click the button it opens text boxes on every category. I just want one text box. Here is the code I have so far. Any help would be appreciated. Thanks
state = { showing: true };
renderLists(categories) {
const { showing } = this.state;
if (categories == null) return <div />;
return (
<ul className="ul">
{categories.map(category => {
return (
<li id={category._id} className="categories" key={category._id}>
{category.name}
<Posts categ={category._id} />
<button
className="label"
onClick={() => this.setState({ showing: !showing })}
>
Add Comment
</button>
{showing ? (
<div>
<form method="post" action="/post/create-posts">
<input type="text" name="body" />
<input type="hidden" name="cat" value={category._id} />
<input type="submit" value="Submit" />
</form>
</div>
) : null}
</li>
);
})}
</ul>
);
}
render() {
return (
<div>
<main className="categories">
{this.renderLists(this.state.category)}
</main>
</div>
);
}
Since you are controlling render of the form with a single state value, ever form for every item renders the form when the state value changes.
You should set a unique value on state to show single form every time.
Saving currently active items id to state and checking if that item is active or not can be a simple solution. This also ensures to only single form to be active. If you need to enable multiple forms to be active, you can change below code to hold an array of ids and checking if the id exist in array or not. This implementation also requires you to remove the id from array on a second click to remove form for that item.
Sample
state = { showing: ''};
renderLists(categories) {
const { showing } = this.state;
if (categories == null) return <div />;
return (
<ul className="ul">
{categories.map(category => {
return (
<li id={category._id} className="categories" key={category._id}>
{category.name}
<Posts categ={category._id} />
<button
className="label"
{/* Save item's id to state */}
onClick={() => this.setState({ showing: category._id })}
>
Add Comment
</button>
{/* check item's id */}
{(showing === category._id) ? (
<div>
<form method="post" action="/post/create-posts">
<input type="text" name="body" />
<input type="hidden" name="cat" value={category._id} />
<input type="submit" value="Submit" />
</form>
</div>
) : null}
</li>
);
})}
</ul>
);
}
You can set the dynamic state for each of your category. I have made it below. Initially it was not there in state. On clicking the button, it will update the state with new key(such as categoryName) as true. I have fixed it as toggle. If the required key is true then it will become false.
item.state = { showing: true };
renderLists(categories) {
const { showing } = this.state;
if (categories == null) return <div />;
return (
<ul className="ul">
{categories.map(category => {
let categoryName = category.name // get the categoryName in variable
return (
<li id={category._id} className="categories" key={category._id}>
{category.name}
<Posts categ={category._id} />
//You can also access the object values by using bracket ([]) notation. You can set the dynamic state with unique key, may be we use name as key here
<button
className="label"
onClick={() => {(this.state[categoryName] != undefined) ? (this.state[categoryName] ? this.setState({ [categoryName]: false }) : this.setState({ [categoryName]: true })) : this.setState({ [categoryName]: true })} }
>
Add Comment
</button>
{this.state[categoryName] ? (
<div>
<form method="post" action="/post/create-posts">
<input type="text" name="body" />
<input type="hidden" name="cat" value={category._id} />
<input type="submit" value="Submit" />
</form>
</div>
) : null}
</li>
);
})}
</ul>
);
}
render() {
return (
<div>
<main className="categories">
{this.renderLists(this.state.category)}
</main>
</div>
);
}

redux-form multiple checkbox, values in a single array without bool values

I know that checkbox receive a bool value, so what can i do to change this with multiple checkbox.
below is where i have my all checkbox, the JSX file, each checkbox is a category and user can choose one or more categories
<div className='columns is-multiline'>
{this.props.propsCategories.data.list.map((category, i) => (
<div key={ i } className='column is-one-quarter'>
<Field
name='category[]'
label={ category.title }
component={ WrapperInputCheckbox }
setCategory={(e) => this.handleChange(e, category.id)}
/>
</div>
))}
</div>
in my handleChange i'm just trying change a single value.
//handleChange
handleChange = (e, id) => {
e.target.value = id
console.log(e.target.value);
}
but when i submit my form in category array i get
Category: Array[0]
"" : true
length : 0
the value still a bool :X
i need the value become a array like
category['id-1', 'id-n']
You can create a compound name in the id using the name of the input or passing a prop with other word.
Also you should use map, is useful when you want to apply the function to every item, also it's simpler and more concise than using a for loop to build a list.
<div className={classNameContentGroup}>
{ items.map((item) => (
<div className={`${classNameContent } `"}>
<input
{...input}
name={name}
type="checkbox"
value={ item.value }
className={className}
id={ `${name}-${item.value}` }
/>
<label className={classNameLabel} htmlFor={ `${ nameR}-${item.value}` }>
{item.label}
</label>
</div>
))
}
</div>

Resources