How to get Redux Dynamic form values without specifying the name - reactjs

I am generating my form by JSON data. Form rendering is working fine. Now i want to get the form values on submit something like below.So that i can call an API to save those values in the same submit event.
{"elementname1":"value1",
"elementname2":"value2"
"elementname2":"value2"}
Is there any way to get the values like that .
i have tried with formValueSelector but we have to specify the element name to get the value.
Here is my code
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { getTemplateData } from './actions';
import autoBind from 'react-autobind';
import { Scrollbars } from 'react-custom-scrollbars';
import { getFormValues, Field, reduxForm } from 'redux-form';
import { formValueSelector } from 'redux-form';
function mapDispatchToProps(dispatch) {
return {
loadData() {
return dispatch(getTemplateData())
}
}
}
function mapStateToProps(state,ownProps) {
const selector = formValueSelector('create-template-form')
return {
...state,
templateData: state.templateData
}
}
class createTemplate extends Component {
constructor(props, context) {
super(props, context);
autoBind(this);
this.state = {
topTabSelected: "details",
leftTabSelected: "details",
tabIndex: 0,
dataIndex: 0
};
this.gotoGenRulesState = this.gotoGenRulesState.bind(this);
}
gotoGenRulesState = (data) => {
this.props.dispatch({ type: 'FORM_SUBMITTED',data});
}
componentDidMount() {
this.props.loadData();
}
render() {
const renderField = ({ field, detailType}) => {
const { type, placeholder } = field[detailType];
if (type === 'text' || type === 'email' || type === 'number' || type === 'checkbox') {
return <input name={detailType + field.key} {...field.input} className="form-control" placeholder={placeholder} type={type} />
} else if (type === 'select') {
const { options } = field
return (
<select name={field.name} className="form-control" onChange={field.input.onChange}>
{options.map((option, index) => {
return <option key={index} value={option.value}>{option.label}</option>
})}
</select>
)
} else {
return <div>Type not supported.</div>
}
}
return (
<form onSubmit={this.gotoGenRulesState}>
<div>
<div className="row" style={{ padding: "30px 80px" }} >
<div className="padding-sides-none col-md-12">
<h3 className="purple-color">Create Template</h3>
<div className="tabbable">
{this.props.templateData &&
<ul className="nav nav-tabs tab-nav toptab">
{this.props.templateData.map((data, index) => (
<li className={`${data.key === this.state.topTabSelected ? 'active' : ''}`} key={data.key + index} style={{ cursor: 'pointer' }} ><a onClick={() => this.openTopTab(data.key, index)}
data-toggle="tab">{data.name}</a></li>
))}
</ul>
}
<div className="">
{this.props.templateData && this.props.templateData[this.state.tabIndex] &&
<div className="tabbable tabs-left row">
<div className="padding-sides-none col-md-4 tab-sec">
<ul className="nav tab-nav">
{this.props.templateData[this.state.tabIndex].subnodes.map((data, index) => (
<li key={data.key + index} style={{ cursor: 'pointer' }} ><a className={`${data.key === this.state.leftTabSelected ? 'detailsTitleActive' : ''}`} onClick={() => this.openDetailsTab(data.key, index)}
data-toggle="tab">{data.name}</a></li>
))}
</ul>
</div>
<div className="col-md-8 padding-sides-none inner-tab-content">
<Scrollbars style={{ height: 400 }}>
<div className="tab-content">
{this.props.templateData[this.state.tabIndex].subnodes[this.state.dataIndex].subnodes.map((field, index) => (
<div className={`form-group ${field.type === 'CompleteDetail' ? 'dynamic-field-inline' : ''}`} key={field.name}>
<label>{field.name}</label>
{field.detail &&
<span>
{field.type === 'CompleteDetail' && <label>Details</label>}
<Field
name={`details${field.key}`}
component={renderField}
field={field}
detailType='detail'
/>
</span>
}
{field.range &&
<span>
{field.type === 'CompleteDetail' && <label>Range</label>}
<Field
name={'range' + field.key}
component={renderField}
field={field}
detailType='range'
/>
</span>
}
{field.operator &&
<span>
{field.type === 'CompleteDetail' && <label >Operator</label>}
<Field
name={'operator' + field.key}
component={renderField}
field={field}
detailType='operator'
/>
</span>
}
</div>
))}
</div>
</Scrollbars>
</div>
</div>
}
</div>
</div>
</div>
</div>
</div>
</form>
)
}
}
createTemplate = reduxForm({
form: 'create-template-form', // a unique identifier for this form
})(createTemplate)
createTemplate = connect(
mapStateToProps,
mapDispatchToProps
)(createTemplate)
export default createTemplate;

Using redux form you can get all form values with state.form.create-template-form.
By the way you should rename your form identifier with camelCase :
connect(
state => ({
myFormValues: state.form.createTemplateForm,
}),
)
And you will acces this value thanks to props this.props.myFormValues

If you want to use values when submitting, you can also do that :
submitFunction = (data) => {
console.log(data) // you can use your form data here, and you don't need form name
};
render() {
return (
<form onSubmit={this.props.handleSubmit(this.submitFunction)}>

Related

How can I maintain the order of adding items in react web app?

How can I create an order of adding items. I should be able to add one after the other item during on click. By default it should display the Textarea first and then blockquote ( see below )
a) When a user click on Text area button, it should add one after the blockquote.
b) Then when the user clicks on Code area button, it should add after Textarea. Could someone please advise ?
CSB link: https://codesandbox.io/s/distracted-worker-26jztf?file=/src/App.js
Something similar >> Expected behaviour: https://jsfiddle.net/nve8qdbu/8/
import "./styles.css";
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useForm } from "react-hook-form";
const blogListData = [
{
id: 1,
heading: "React state",
date: "22-May-2022",
tag: "React",
count: "3"
},
{
id: 2,
heading: "Cypress testing detailss",
date: "22-May-2022",
tag: "Cypress",
count: "5"
}
];
const Admin = () => {
const [createImageTag, setImageTag] = useState("");
const [fields, setFields] = useState([{ value: null }]);
const [createCode, setCreateCode] = useState([{ value: null }]);
const [blogList, setBlogList] = useState([]);
const {
register,
handleSubmit,
formState: { errors },
reset
} = useForm();
useEffect(() => {
setBlogList(blogListData);
}, []);
function handleChangeTextArea(i, event) {
const values = [...fields];
values[i].value = event.target.value;
setFields(values);
}
function handleChangeCode(i, event) {
const codeValues = [...createCode];
codeValues[i].value = event.currentTarget.innerText;
setCreateCode(codeValues);
}
function handleTextAreaAdd() {
const values = [...fields];
values.push({ value: null });
setFields(values);
}
function handleCodeAreaAdd() {
const codeValues = [...createCode];
codeValues.push({ value: null });
setCreateCode(codeValues);
}
function handleImageAreaAdd() {
const image = [...createImageTag];
image.push({ value: null });
setCreateCode(image);
}
function handleRemoveText(i) {
const values = [...fields];
values.splice(i, 1);
setFields(values);
}
function handleRemoveCode(i) {
const codeValues = [...createCode];
codeValues.splice(i, 1);
setCreateCode(codeValues);
}
const handleLogout = () => {
localStorage.removeItem("loginEmail");
};
return (
<div id="App">
<div className="parent">
<div className="adminSection">
<h1>Create a new blog</h1>
<div className="row">
<div className="logout">
<img
src="/images/logout.png"
alt="Logout"
onClick={handleLogout}
></img>
</div>
<div className="createBlogSection">
<div className="row">
<button
onClick={() => handleTextAreaAdd()}
className="textAreaBtn"
>
Text Area
</button>
<button
onClick={() => handleCodeAreaAdd()}
className="codeAreaBtn"
>
Code Area
</button>
<button
onClick={() => handleImageAreaAdd()}
className="imageAreaBtn"
>
Add Image
</button>
</div>{" "}
<br></br>
<div className="row">
{fields.map((field, idx) => {
return (
<div key={`${field}-${idx}`} className="dtextArea">
<button
type="button"
onClick={() => handleRemoveText(idx)}
className="closeElement"
>
X
</button>
<textarea
type="text"
id="blogtext"
placeholder="Enter your text here"
className="defaultTextArea"
{...register("blogtext", {
required: true,
minLength: {
value: 25,
message: "Minimum length of 25 letters"
}
})}
value={field.value || ""}
onChange={(e) => handleChangeTextArea(idx, e)}
/>
<span className="validationmsg">
{errors.blogtext &&
errors.blogtext.type === "required" && (
<span>Blog text is required !</span>
)}
{errors.blogtext && (
<span>{errors.blogtext.message}</span>
)}
</span>
</div>
);
})}
</div>
<div className="row">
{createCode.map((code, idx) => {
return (
<div key={`${code}-${idx}`} className="dCodeArea">
<button
type="button"
onClick={() => handleRemoveCode(idx)}
className="closeElement"
>
X
</button>
<blockquote
type="text"
id="blogCode"
contentEditable="true"
className="codehighlight"
placeholder="Enter your code here"
{...register("blogCode", {
required: true
})}
value={code.value || ""}
onInput={(e) => handleChangeCode(idx, e)}
/>
</div>
);
})}
</div>
<div className="row">
</div>
<div className="row">
<div className="submitSection">
<input type="submit" className="submitBtn" />
</div>
</div>
</div>
</div>
</div>
<div className="blogListSection">
<h1>Edit blogs</h1>
<div className="row">
<div className="editBlogSection">
{blogList.map(({ id, heading, count }) => (
<a
key={id}
href="https://www.testingsite.com/"
className="blogitems"
>
<pre>
<span>{count}</span> {heading}
</pre>
</a>
))}
</div>
</div>
</div>
</div>
</div>
);
};
export default Admin;
react is designed for components . each of your list elements should be refactored by a component.then it would be easier. i think a single react component could do the trick

Errors while trying to convert Class Component to Functional

I'm trying to convert the class components in my Reactjs ToDo app to functional components. Here is one of the my original components:
import React, { Component } from "react";
import TaskDataService from "../services/task.service";
import FadeIn from 'react-fade-in';
import { Link } from "react-router-dom";
export default class TasksList extends Component {
constructor(props) {
super(props);
this.onChangeSearchTitle = this.onChangeSearchTitle.bind(this);
this.retrieveTasks = this.retrieveTasks.bind(this);
this.refreshList = this.refreshList.bind(this);
this.setActiveTask = this.setActiveTask.bind(this);
this.removeAllTasks = this.removeAllTasks.bind(this);
this.searchTitle = this.searchTitle.bind(this);
this.state = {
Tasks: [],
currentTask: null,
currentIndex: -1,
searchTitle: ""
};
}
componentDidMount() {
this.retrieveTasks();
}
onChangeSearchTitle(e) {
const searchTitle = e.target.value;
this.setState({
searchTitle: searchTitle
});
}
retrieveTasks() {
TaskDataService.getAll()
.then(response => {
this.setState({
Tasks: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
refreshList() {
this.retrieveTasks();
this.setState({
currentTask: null,
currentIndex: -1
});
}
setActiveTask(Task, index) {
this.setState({
currentTask: Task,
currentIndex: index
});
}
removeAllTasks() {
TaskDataService.deleteAll()
.then(response => {
console.log(response.data);
this.refreshList();
})
.catch(e => {
console.log(e);
});
}
searchTitle() {
this.setState({
currentTask: null,
currentIndex: -1
});
TaskDataService.findByTitle(this.state.searchTitle)
.then(response => {
this.setState({
Tasks: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
render() {
const { searchTitle, Tasks, currentTask, currentIndex } = this.state;
return (
<FadeIn>
<div className="list row">
<div className="col-md-8">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by title"
value={searchTitle}
onChange={this.onChangeSearchTitle}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={this.searchTitle}
>
Search
</button>
</div>
</div>
</div>
<div className="col-md-6">
<h4>Tasks List</h4>
<FadeIn>
<ul className="list-group">
{Tasks &&
Tasks.map((Task, index) => (
<li
className={
"list-group-item " +
(index === currentIndex ? "active" : "")
}
onClick={() => this.setActiveTask(Task, index)}
key={index}
>
<div className="align-left">
{Task.title}
</div>
<div className="align-right">
{Task.startDate.toString().split("T")[0]}
</div>
</li>
))}
</ul>
</FadeIn>
<button
className="m-3 btn btn-sm btn-danger"
onClick={this.removeAllTasks}
>
Remove All
</button>
</div>
<div className="col-md-6">
{currentTask ? (
<FadeIn>
<div>
<h4>Task</h4>
<div>
<label>
<strong>Title:</strong>
</label>{" "}
{currentTask.title}
</div>
<div>
<label>
<strong>Description:</strong>
</label>{" "}
{currentTask.description}
</div>
<div>
<label>
<strong>Status:</strong>
</label>{" "}
{currentTask.completed ? "Completed" : "Pending"}
</div>
<div>
<label>
<strong>Due Date:</strong>
</label>{" "}
{currentTask.startDate.split("T")[0]}
</div>
<Link
to={"/Tasks/" + currentTask.id}
className="badge badge-warning"
>
Edit
</Link>
</div>
</FadeIn>
) : (
<div>
<br />
<p>Please click on a Task...</p>
</div>
)}
</div>
</div>
</FadeIn>
);
}
}
...and my attempt to convert it into a functional component:
import React, { useState, useEffect } from "react";
import TaskDataService from "../services/task.service";
import FadeIn from 'react-fade-in';
import { Link } from "react-router-dom";
function TasksList() {
const [Tasks, setTasks] = useState([]);
const [currentTask, setCurrentTask] = useState(null);
const [currentIndex, setCurrentIndex] = useState(-1);
const [searchTitle, setSearchTitle] = useState("");
useEffect(() => {
retrieveTasks();
});
const onChangeSearchTitle = (e) => {
const searchTitle = e.target.value;
setSearchTitle({
searchTitle: searchTitle
});
}
const retrieveTasks = () => {
TaskDataService.getAll()
.then(response => {
setTasks({
Tasks: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
const refreshList = () => {
retrieveTasks();
setTasks(null);
setCurrentIndex(-1);
}
const setActiveTask = (Task, index) => {
setTasks( Task );
setCurrentIndex( index );
}
const removeAllTasks = () => {
TaskDataService.deleteAll()
.then(response => {
console.log(response.data);
refreshList();
})
.catch(e => {
console.log(e);
});
}
const onSearchTitle = () => {
setTasks(null);
setCurrentIndex(-1);
TaskDataService.findByTitle(searchTitle)
.then(response => {
setTasks(response.data)
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
return (
<FadeIn>
<div className="list row">
<div className="col-md-8">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by title"
value={searchTitle}
onChange={onChangeSearchTitle}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={onSearchTitle}
>
Search
</button>
</div>
</div>
</div>
<div className="col-md-6">
<h4>Tasks List</h4>
<FadeIn>
<ul className="list-group">
{Tasks &&
Tasks.map((Task, index) => (
<li
className={
"list-group-item " +
(index === currentIndex ? "active" : "")
}
onClick={() => setActiveTask(Task, index)}
key={index}
>
<div className="align-left">
{Task.title}
</div>
<div className="align-right">
{Task.startDate.toString().split("T")[0]}
</div>
</li>
))}
</ul>
</FadeIn>
<button
className="m-3 btn btn-sm btn-danger"
onClick={removeAllTasks}
>
Remove All
</button>
</div>
<div className="col-md-6">
{currentTask ? (
<FadeIn>
<div>
<h4>Task</h4>
<div>
<label>
<strong>Title:</strong>
</label>{" "}
{currentTask.title}
</div>
<div>
<label>
<strong>Description:</strong>
</label>{" "}
{currentTask.description}
</div>
<div>
<label>
<strong>Status:</strong>
</label>{" "}
{currentTask.completed ? "Completed" : "Pending"}
</div>
<div>
<label>
<strong>Due Date:</strong>
</label>{" "}
{currentTask.startDate.split("T")[0]}
</div>
<Link
to={"/tasks/" + currentTask.id}
className="badge badge-warning"
>
Edit
</Link>
</div>
</FadeIn>
) : (
<div>
<br />
<p>Please click on a Task...</p>
</div>
)}
</div>
</div>
</FadeIn>
);
}
export default TasksList;
But when running my frontend, the page fails to load with this error seeming like the big one: "Uncaught TypeError: Tasks.map is not a funtion". Console points to this segment as the culprit:
<ul className="list-group">
{Tasks &&
Tasks.map((Task, index) => (
<li...
So it doesn't like Tasks.map. I've spent a lot of time on research and can't figure out what the problem is. Any ideas?
It seems that you are settings in multiple part of your code with setTasks a value other than an array which eventually can leads to Tasks.map triggering an error.
For exemple in the function retreiveTasks you are setting Tasks to an object.

How do I perform a filter function on data from an Axios API call in React?

I am having trouble displaying my yogaCard in the return statement. When I call it as {yogaCard} I get a message saying it is undefined.
if (yogas.length > 0) {
let yogaCard = yogas.filter((yoga) => {
if (searchPose === "") {
return yoga
}
else if (
yoga.english_name.toLowerCase().includes(searchPose.toLocaleLowerCase())) {
return yoga
}
}).map((yoga) => (
<div key={yoga.id} style={{width: '20%' }} className="card"
>
<img className='yogaphoto' src={yoga.img_url}/>
<div className="card-body">
<h2>{yoga.english_name}</h2>
</div>
</div>
))
}
return (
<>
<div className="yoga">
<div className='yogasearch'>
<h1>Yoga Poses</h1>
<input type="text" class="input" placeholder="Search by pose" onChange={event => {setSearchPose(event.target.value)}}/>
</div>
<div className="row">
{yogaCard}
</div>
</div>
</>
)
}
export default IndexYogas
You're not showing how you're retrieving your yogas data from axios or any way to debug your current issue, but I'd recommend passing your data retrieved from axios to a child component that contains all those div boxes and formatting, etc.
function MainPage() {
const [yogas, setYogas] = React.useState([]);
useEffect(() => {
// axios call to populate data
requestYogaInfo().then((resp) => {
setYogas(resp.data); // this depends on the shape of your webservice response
});
}, []);
return (
<>
<div className="yoga">
<div className='yogasearch'>
<h1>Yoga Poses</h1>
<input type="text" class="input" placeholder="Search by pose" onChange={event => {setSearchPose(event.target.value)}}/>
</div>
<div className="row">
{yogas && yogas.length > 0
? (
yogas.filter((yoga) => {
if (searchPose === "") {
return yoga
}
else if (
yoga.english_name.toLowerCase().includes(searchPose.toLocaleLowerCase())) {
return yoga
}
}).map((yoga) => (
<div key={yoga.id} style={{width: '20%' }} className="card"
>
<img className='yogaphoto' src={yoga.img_url}/>
<div className="card-body">
<h2>{yoga.english_name}</h2>
</div>
</div>
))
)
: null
}
</div>
</div>
</>
)
}

I want to use react hook 'useState' to save the information fetched from API and show on the scrren. But, I can't use in class so what can I do?

So, for some context I'm fetching some search results on click on search button. The result shows in console.log() after clicking search button perfectly. the SearchComponent.js is below.
import React, { Component, useState } from 'react'
import { API_KEY, API_URL } from '../config/keys';
import { Link } from 'react-router-dom';
class SearchBox extends Component {
constructor(props) {
super(props);
this.state = {
searchQuery: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.onChangeValue = this.onChangeValue.bind(this);
}
handleChange = (e) => {
e.preventDefault();
const { name, value } = e.target;
this.setState({ [name]: value });
}
handleSubmit = (e) => {
e.preventDefault();
var data = this.state.searchQuery;
const url = `${API_URL}search/${this.state.selectedOption}?api_key=${API_KEY}&language=en-US&query=${encodeURI(data)}&page=1&include_adult=false`;
fetch(url)
.then(response => response.json())
.then(response => {
console.log(response);
const result = response.results;
})
}
onChangeValue(event) {
this.setState({
selectedOption: event.target.value
});
}
render() {
return (
<>
<div className="mt-3">
{/* Breadcrumb */}
<div style={{ width: '95%', margin: '1rem auto' }}>
<nav aria-label="breadcrumb">
<ol className="breadcrumb">
<li className="breadcrumb-item"><Link to='/'>Home</Link></li>
<li className="breadcrumb-item active" aria-current="page">Search</li>
</ol>
</nav>
</div>
<div className="row">
<div className="col-6 offset-3">
<form className="form-group">
<div className="input-group">
<input
className="form-control"
type="text"
placeholder= 'Search...'
onChange={this.handleChange}
name= 'searchQuery'
value={this.state.searchQuery} />
<button onClick={this.handleSubmit} type="submit" className="btn btn-primary input-group-addon" ><span className="fa fa-search fa-lg"></span></button>
</div>
{/* radio buttons */}
<div className="mt-2">
<div class="form-check">
<input
class="form-check-input"
type="radio"
name="movie"
value="movie"
checked={this.state.selectedOption === "movie"}
onChange={this.onChangeValue}
/>
<span class="form-check-label font-weight-bold">
Movies
</span>
</div>
<div class="form-check">
<input
class="form-check-input"
type="radio"
value="tv"
name="tvshow"
checked={this.state.selectedOption === "tv"}
onChange={this.onChangeValue}
/>
<span class="form-check-label font-weight-bold">
TV Shows
</span>
</div>
</div>
</form>
</div>
</div>
</div>
{/* search results */}
<div style={{ width: '95%', margin: '1rem auto' }}>
<div className="text-center">
<div className="font-weight-lighter h2"> Search Results </div>
</div>
</div>
</>
)
}
}
export default SearchBox;
the array of result is in result variable of handleSubmit(). how can I use useState to store my result var and then show it on scrren.
if you don't understand how i'm talking about using this hook i've attached a file which does similar thing.
landingComponent.js
import React, { useEffect, useState } from 'react';
import {API_URL, API_KEY, IMAGE_URL} from '../../config/keys';
import { Row } from 'reactstrap';
import MainImage from './MainImage';
import GridCard from './GridCard';
import { Link } from 'react-router-dom';
function LandingPage() {
const [Movies, setMovies] = useState([]);
const [CurrentPage, setCurrentPage] = useState(0);
useEffect( () => {
const endpoint = `${API_URL}movie/popular?api_key=${API_KEY}&language=en-US&page=1`;
fetchMovies(endpoint);
}, []);
const fetchMovies = (path) => {
fetch(path)
.then(response => response.json())
.then(response => {
console.log(response);
setMovies([...Movies, ...response.results]);
setCurrentPage(response.page);
})
}
const handleClick = () => {
const endpoint = `${API_URL}movie/popular?api_key=${API_KEY}&language=en-US&page=${CurrentPage + 1}`
fetchMovies(endpoint);
}
return (
<div style={{ width: '100%', margin: 0 }} >
<div style={{ width: '95%', margin: '1rem auto' }}>
{/* Breadcrumbs */}
<nav aria-label="breadcrumb">
<ol className="breadcrumb">
<li className="breadcrumb-item"><Link to='/'>Home</Link></li>
<li className="breadcrumb-item active" aria-current="page">Movies</li>
</ol>
</nav>
<div className="font-weight-bold h2"> Latest Movies </div>
<hr style={{borderColor:'black'}}/>
<Row>
{Movies && Movies.map((movie, index) => (
<React.Fragment key={index}>
<GridCard
image={movie.poster_path && `${IMAGE_URL}w500${movie.poster_path}`}
movieId={movie.id} movieTitle={movie.title} name={movie.original_title}
/>
</React.Fragment>
))}
</Row>
<br />
<div className="text-center">
<button className="btn btn-primary" onClick={handleClick}> Load More </button>
</div>
</div>
</div>
)
}
export default LandingPage
I want to use same way in SearchComponent.js. I tried so many thing but none worked. Help is appreciated.
React has two ways of using components :
Class components
Declared this way : class ComponentName extends Component {
then your state is managed using : this.setState()
Function components
Declared just as a function as your second example
There you can use hooks and the useState()
So if you're not planning to re-write all your component you'll have to use this.setState()
When you fetch the data you need to store it in the state again creating results:[] property in the state.
handleSubmit = (e) => {
e.preventDefault();
var data = this.state.searchQuery;
const url = `${API_URL}search/${this.state.selectedOption}?api_key=${API_KEY}&language=en-US&query=${encodeURI(data)}&page=1&include_adult=false`;
fetch(url)
.then(response => response.json())
.then(response => {
console.log(response);
const result = response.results;
this.setState({results:result}); //Here you store the state.
});
}
Now you will have to show it in the results JSX block.
{/* search results */}
<div style={{ width: '95%', margin: '1rem auto' }}>
<div className="text-center">
<div className="font-weight-lighter h2"> Search Results </div>
<div className="results">
<ul>
{this.state.results.length && this.state.results.map(item => { return (
<li> {item} </li>
) })}
</ul>
</div>
</div>
</div>

Modify prop in list of components generated in a loop

I have a checkbox component that has a disabled property. My goal is:
When 3 checkboxes are selected set the disabled property to true in all checkboxes that are not selected.
Each time a checkbox is selected show an updated count of remaining selection allowed.
For goal 1 I am not sure what the right way to do this is. Do I need to create an array of refs?
For goal 2 I have added this code:
{3 - Object.keys(selected).length + " domains remaining"}
But when selected is updated the render does not get the updated value.
My code with comment where I want to disable/enable all is on codesandbox here: https://codesandbox.io/s/exciting-bush-119ef?file=/src/App.js
Also pasted here below:
import React, { useState } from "react";
export default function App(props) {
const [enableSelection, setEnableSelection] = useState(true);
const [selected, setSelected] = useState({});
let domains = [
"example1.com",
"example2.com",
"example3.com",
"example4.com",
"example5.com",
"example6.com",
"example7.com",
"example8.com",
"example9.com",
"example10.com",
"example11.com",
"example12.com",
"example13.com",
"example14.com",
"example15.com",
"example16.com"
];
function handleChange(index, event) {
console.log(index);
console.log(event.target.name);
console.log(event.target.value);
console.log(event.target.checked);
if (event.target.checked === true) {
setSelected((prev) => {
prev[index] = event.target.value;
return prev;
});
} else {
setSelected((prev) => {
delete prev[index];
return prev;
});
}
if (Object.keys(selected).length >= 3) {
//disable all other input
} else {
//make sure all other enabled
}
}
return (
<div className="">
<div className="">
<SubHead
text={3 - Object.keys(selected).length + " domains remaining"}
/>
{domains.map((domain, i) => (
<div key={i} className="odd:bg-gray-200">
<CheckBox
disabled={false}
label={domain}
name={"checkbox_" + i}
value={domain}
onChange={(index, e) => handleChange(i, e)}
/>
</div>
))}
</div>
</div>
);
}
function CheckBox({
disabled,
key,
label,
selected,
name,
value,
onChange: parentChange
}) {
const [selectedState, setSelectedState] = useState(selected ?? false);
function handleChange(event) {
setSelectedState(!selectedState);
parentChange(key, event);
}
return (
<label key={key} className="inline-flex items-center mt-3">
<input
id={name}
name={name}
value={value}
onChange={handleChange}
type="checkbox"
className="form-checkbox h-5 w-5 text-gray-600"
disabled={disabled}
checked={selected}
/>
<span
className={
"ml-2 " +
(selectedState ? "font-semibold " : "") +
(disabled ? "text-gray-400" : "text-gray-700")
}
>
{label}
</span>
</label>
);
}
function SubHead(props) {
return <h3 className="font-medium text-xl text-center">{props.text}</h3>;
}
The problem was that you handle the event in two places,
You don't need to handle the state of the checkbox twice, once in the parent and another inside it. I also converted the selected to array to make it easier:
Here is the demo: https://codesandbox.io/s/boring-sun-35lfk?file=/src/App.js
import React, { useState } from "react";
export default function App(props) {
const [selected, setSelected] = useState([]);
let domains = [
"example1.com",
"example2.com",
"example3.com",
"example4.com",
"example5.com",
"example6.com",
"example7.com",
"example8.com",
"example9.com",
"example10.com",
"example11.com",
"example12.com",
"example13.com",
"example14.com",
"example15.com",
"example16.com"
];
function handleChange(event) {
console.log(event.target.name);
console.log(event.target.value);
console.log(event.target.checked);
if (event.target.checked) {
setSelected((prev) => [...prev, event.target.name]);
} else {
setSelected((prev) => prev.filter((name) => name !== event.target.name));
}
if (Object.keys(selected).length >= 3) {
//disable all other input
} else {
//make sure all other enabled
}
}
const remaining = 3 - Object.keys(selected).length;
return (
<div className="">
<div className="">
<SubHead text={remaining + " domains remaining"} />
{domains.map((domain) => {
const checked = selected.indexOf("checkbox_" + domain) > -1;
return (
<div key={domain} className="odd:bg-gray-200">
<CheckBox
checked={checked}
disabled={!checked && remaining === 0}
label={domain}
name={"checkbox_" + domain}
value={domain}
onChange={handleChange}
/>
</div>
);
})}
</div>
</div>
);
}
function CheckBox({
checked,
disabled,
key,
label,
selected,
name,
value,
onChange: parentChange
}) {
return (
<label key={key} className="inline-flex items-center mt-3">
<input
id={name}
name={name}
value={value}
onChange={parentChange}
type="checkbox"
className="form-checkbox h-5 w-5 text-gray-600"
disabled={disabled}
checked={selected}
/>
<span
className={
"ml-2 " +
(checked ? "font-semibold " : "") +
(disabled ? "text-gray-400" : "text-gray-700")
}
>
{label}
</span>
</label>
);
}
function SubHead(props) {
return <h3 className="font-medium text-xl text-center">{props.text}</h3>;
}

Resources