I am using react, and i need to add files in a list.
this.state = {
some: {
names: [],
keys: 0
}
file: {
images: []
}
}
newSome = () => {
const some = {
...this.state.some,
['name'] = 'inputed name',
[keys] = this.state.some.keys++
}
handleChange = (field,key) => event => {
const file = {...this.state.file}
file['images'].push({[key] : event.target.files[event.target.files.length-1]})
this.setState({file})
}
render(){
const {key} = this.state.some
return (
<input type='file' onChange={this.handleChange('images', key)}/>
)
}
i have a dictionary that has as "name with image" and "name without image". in images, i have the images of names. For example: name['maria', 'joao', renato], and joao doesn't have image... so in images i have images[{0: file0},{2: file1}]. When i load a image, in first operation, it works, but in second doesn't work, and appear the error TypeError: file.images.push is not a function. What i have to do?
state = {
some: {
names: [],
key: ""
},
file: {
images: []
}
};
handleChange = (field, key) => event => {
const { files } = event.target;
const file = {
images: [...this.state.file.images, { [key]: files[files.length - 1] }]
};
this.setState({ file });
};
render() {
const { key } = this.state.some;
return <input type="file" onChange={this.handleChange("images", key)} />;
}
Related
I'm new React developer(mainly with hooks but did not find good example with hooks), here i have antd table with search functionality, my question is when user writes something in search then user gets different result, how to cancel that search by clicking 'Reset' button ?
my code:
https://codesandbox.io/s/antd-table-filter-search-forked-mqhcn?file=/src/EventsSection/EventsSection.js
You can add an id to your input into TitleSearch.js:
<Search
id='IDYOUWANT'
placeholder="Enter Title"
onSearch={onSearch}
onChange={onChange}
style={{ width: 200 }}
/>
And add event into EventsSection.js
ResetInput = () => {
const input = document.getElementById('IDYOUWANT');
input.value = '';
this.handleSearch('');
}
....
<button
onClick={this.ResetInput}
>Reset</button>
Change IDYOUWANT with your id
run this code
Created a new function for reset value and trigger it from reset button.
function:
resetValue = () =>{
this.setState({
eventsData: eventsData
});
}
And trigger from button
<button onClick={this.resetValue}>Reset</button>
all code::
import React, { Component } from "react";
import styles from "./style.module.css";
import { EventsTable } from "../EventsTable";
import { StatusFilter } from "../StatusFilter";
import { TitleSearch } from "../TitleSearch";
const eventsData = [
{
key: 1,
title: "Bulletproof EP1",
fileType: "Atmos",
process: "match media",
performedBy: "Denise Etridge",
operationNote: "-",
updatedAt: "26/09/2018 17:21",
status: "complete"
},
{
key: 2,
title: "Dexter EP2",
fileType: "Video",
process: "Compliance",
performedBy: "Dane Gill",
operationNote: "passed",
updatedAt: "21/09/2018 12:21",
status: "inProgress"
}
];
class EventsSection extends Component {
constructor(props) {
super(props);
this.state = {
eventsData
};
}
handleFilter = (key) => {
const selected = parseInt(key);
if (selected === 3) {
return this.setState({
eventsData
});
}
const statusMap = {
1: "complete",
2: "inProgress"
};
const selectedStatus = statusMap[selected];
const filteredEvents = eventsData.filter(
({ status }) => status === selectedStatus
);
this.setState({
eventsData: filteredEvents
});
};
handleSearch = (searchText) => {
const filteredEvents = eventsData.filter(({ title }) => {
title = title.toLowerCase();
return title.includes(searchText);
});
this.setState({
eventsData: filteredEvents
});
};
handleChange = (e) => {
const searchText = e.target.value;
const filteredEvents = eventsData.filter(({ title }) => {
title = title.toLowerCase();
return title.includes(searchText);
});
this.setState({
eventsData: filteredEvents
});
};
resetValue = () =>{
this.setState({
eventsData: eventsData
});
}
render() {
return (
<section className={styles.container}>
<header className={styles.header}>
<h1 className={styles.title}>Events</h1>
<button onClick={this.resetValue}>Reset</button>
<TitleSearch
onSearch={this.handleSearch}
onChange={this.handleChange}
className={styles.action}
/>
</header>
<EventsTable eventsData={this.state.eventsData} />
</section>
);
}
}
export { EventsSection };
Here is what i did in order to solve it:
i added onClick on the button
<button onClick={this.resetSearch}>Reset</button>
Then in the function i put handleSearch to '', by doing this it reset the table:
resetSearch = () =>{
this.handleSearch('')
}
I am creating warehouse management application where I enter data to the form in the form component. When submitting form I want to render entered data to the DOM, in the local storage: http://localhost:3000/products/. changeHandler works good, I receive data from input fields. However, addItemHandler does not work and it does not render anything. Could anybody help me, please.
import React, { Component, createContext } from "react";
import nextId from "react-id-generator";
const ProductContext = createContext();
class ProductProvider extends Component {
newId = nextId();
state = {
products: [
{ id: "", name: "", ean: "", type: "", weight: "", color: "", quantity: "", price: "", info: "", }, ],
detailProduct: "",
};
componentDidMount() {
this.setProducts();
}
setProducts = () => {
let products = [];
this.state.products.forEach((item) => {
const singleItem = { ...item };
products = [...products, singleItem];
});
this.setState(() => {
return { products: products };
});
};
getItem = (id) => {
const product = this.state.products.find((item) => item.id === id);
return product;
};
productDetailHandler = (id) => {
const product = this.getItem(id);
this.setState(() => {
return { detailProduct: product };
});
};
changeHandler = (event) => {
const value = event.target.value;
this.setState({
products: { ...this.state.products, [event.target.name]: value },
});
};
addItemHandler = ( event, name, ean, type, weight, color, quantity, price, info ) => {
event.preventDefault();
const products = [
...this.state.products,
{ name, ean, type, weight, color, quantity, price, info, id: this.newID },
];
this.setState({ products: products });
console.log(this.state);
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
productDetailHandler: this.productDetailHandler,
changeHandler: this.changeHandler,
addItemHandler: this.addItemHandler,
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
const ProductConsumer = ProductContext.Consumer;
export { ProductProvider, ProductConsumer };
state = {
...
newProductItem: {}
};
// the `products`'s type is an Object Array
changeHandler = (event) => {
const value = event.target.value;
const {newProductItem} = this.state
this.setState({
newProductItem: { ...newProductItem, [event.target.name]: value}
});
}
addItemHandler = (event) => {
event.preventDefault();
const {newProductItem} = this.state
const products = [
...this.state.products,
{...newProductItem, id:this.newID}
];
this.setState({
products,
newProductItem: {}
});
};
==== in diff page ==
// form.jsx
state = {
...
newProductItem: {}
};
...
changeHandler = (event) => {
const value = event.target.value;
const { newProductItem } = this.state
this.setState({
newProductItem: { ...newProductItem, [event.target.name]: value}
});
}
...
render(){
...
const { addItemHandler} = this.props
const { newProductItem } = this.state
<Form submit={(ev)=> {
addItemHandler(ev, newProductItem);
this.setState({newProductItem:{}});
}>
<Input onChange={changeHandler} />
</Form>
}
...
<>
...
// main.jsx
addItemHandler = (event, newProductItem) => {
event.preventDefault();
const products = [
...this.state.products,
{...newProductItem, id:this.newID}
];
this.setState({
products
});
};
Front End - Front End
Upon clicking the star, I want to update the state of nested object, with the new rating value of star.
I tried many things but it didnt work as states are immutable.
Nested State
Can some upon please suggest how can I update the value in nested object
onStarClicked = (kTypName, subItemId1, newRating) => {
//console.log(subItemId.split("_"));
let evaluation = subItemId1.split("_")[0];
let subItemId = subItemId1.split("_")[1];
console.log(subItemId);
const r = { ...this.state.ratings };
let kT = r.knowledgeTypes;
let sub = '', kTN = '', kIN = '';
kT.map(knowledgeType => {
//console.log(knowledgeType.knowledgeTypeId);
knowledgeType.knowledgeItems.map(knowledgeItem => {
//console.log(knowledgeItem.knowledgeItemId);
knowledgeItem.subItems.map(knowledgeSubItem => {
//console.log(knowledgeSubItem.subItemId);
if (subItemId === knowledgeSubItem.subItemId) {
kTN = knowledgeType.knowledgeTypeName;
kIN = knowledgeItem.knowledgeItemName;
sub = knowledgeSubItem;
if (evaluation === "self") {
sub.evaluation.self.rating = newRating;
}
else if (evaluation === "evaluator") {
sub.evaluation.evaluator.rating = newRating;
}
//alert(evaluation + subItemId + ' ' + newRating);
//return;
}
})
})
});
this.setState({
...this.state,
ratings: {
...this.state.ratings,
knowledgeTypes: [
...this.state.ratings.knowledgeTypes,
this.state.ratings.knowledgeTypes.filter(kt => kt.knowledgeTypeName !== kTN),
{
...this.state.ratings.knowledgeTypes.knowledgeItems.
filter(ki => ki.knowledgeItemName !== kIN),
knowledgeItems: {
...this.state.ratings.knowledgeTypes.knowledgeItems.subItems.
filter(si => si.subItemId !== subItemId),
sub
}
}]
}
});
}
You basically have to create a new empty array of knowledgeTypes and use the current state to find which item of the state you need to change using Object.keys/map/filter functions.
You'd use the current state in a variable and modify that variable only. You'd likely not mess with the actual state object in any way.
After you have done that, simply append it to the empty array. Then you can setState() the new array to the actual state property.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
financialYear: "2019-20",
quarter: "Q1",
isCurrentQuarter: true,
knowledgeTypes: [
{
knowledgeTypeName: "Technology",
knowledgeItems: [
{
knowledgeItemName: "Java",
subItems: [
{
subItemId: "2",
subItemName: "Collections",
evaluation: {
self: {
ntnet: "Joe",
rating: 1,
isEditable: true
}
}
}
]
}
]
}
]
};
}
handleClick = e => {
const { knowledgeTypes } = this.state;
// transformation
const itemToChange = knowledgeTypes.map(item => {
if (item.knowledgeTypeName === "Technology") {
return item;
}
});
const currItems = itemToChange[0].knowledgeItems[0].subItems;
const subItem = currItems.map(item => {
if (item.subItemId === "2") {
return item;
}
});
const person = subItem[0].evaluation;
person.self.rating = 55; //change
const newKnowledgeTypes = [];
knowledgeTypes.map(item => {
if (item.knowledgeTypeName === "Technology") {
newKnowledgeTypes.push(itemToChange);
}
newKnowledgeTypes.push(item);
});
this.setState({
knowledgeTypes: newKnowledgeTypes
});
console.log(this.state);
};
render() {
return (
<div>
MyComponent
<button onClick={this.handleClick}>Hello</button>
</div>
);
}
}
The sandbox can be found on https://codesandbox.io/s/musing-dew-8r2vk.
Note: It is advisable you do not use nested state objects because state objects are something more lightweight so that they do not have performance considerations.
import React, { Component } from 'react';
import Auxilary from '../../../hoc/Auxilary/auxilary';
import KnowledgeItems from '../KnowledgeItems/KnowledgeItems';
import Tabs from 'react-bootstrap/Tabs';
import Tab from 'react-bootstrap/Tab';
import knowledge from '../../../assests/staticdata.json';
import './QuarterLog.css';
class QuarterLog extends Component {
constructor() {
super();
this.state = {
"financialYear": "",
"quarter": "",
"isCurrentQuarter": "",
"knowledgeTypes": []
}
}
onStarClicked = (kTypName, kItemName, subItemIdName, newRating) => {
let evaluation = subItemIdName.split("_")[0];
let subItemId = subItemIdName.split("_")[1];
const { knowledgeTypes } = this.state;
// transformation
let knowledgeTypeToChange = knowledgeTypes.map(kType => {
if (kType.knowledgeTypeName === kTypName) {
return kType;
}
});
knowledgeTypeToChange = knowledgeTypeToChange.filter(function (element) {
return element !== undefined;
});
console.log(knowledgeTypeToChange[0]);
let knowledgeItemToChange = knowledgeTypeToChange[0].knowledgeItems.map(item => {
if (item.knowledgeItemName === kItemName) {
return item;
}
});
knowledgeItemToChange = knowledgeItemToChange.filter(function (element) {
return element !== undefined;
});
let knowledgeSubItem = knowledgeItemToChange[0].subItems.map(subItem => {
if (subItem.subItemId === subItemId) {
return subItem;
}
});
knowledgeSubItem = knowledgeSubItem.filter(function (element) {
return element !== undefined;
});
console.log(knowledgeSubItem);
let personEvaluations = knowledgeSubItem[0].evaluation;
if (evaluation === "self") {
personEvaluations.self.rating = newRating.toString(); //change
}
else if (evaluation === "evaluator") {
personEvaluations.evaluator.rating = newRating.toString(); //change
}
const newKnowledgeTypes = [];
knowledgeTypes.map(item => {
if (item.knowledgeTypeName === kTypName) {
newKnowledgeTypes.push(knowledgeTypeToChange[0]);
}
else
newKnowledgeTypes.push(item);
});
this.setState({
knowledgeTypes: newKnowledgeTypes
});
console.log(this.state);
}
componentDidMount() {
// TODO: remove staticdata.js and call REST API and set the response in state
this.setState({
...this.state,
"financialYear": knowledge.financialYear,
"quarter": knowledge.quarter,
"isCurrentQuarter": knowledge.isCurrentQuarter,
"knowledgeTypes": knowledge.knowledgeTypes
})
}
onSubmitRatings = () => {
console.log(this.state);
}
render() {
let data = knowledge; //remove this code, once REST API is implemented
const posts = this.state.knowledgeTypes.map(knowledgeType => {
return (
<Tab key={knowledgeType.knowledgeTypeName} eventKey={knowledgeType.knowledgeTypeName}
title={knowledgeType.knowledgeTypeName}>
<KnowledgeItems
kTypeName={knowledgeType.knowledgeTypeName}
kItems={knowledgeType.knowledgeItems}
ratings={this.state.ratings}
onstarclicked={this.onStarClicked}
/>
</Tab>)
});
return (
<Auxilary>
<div className="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<div><h1>Financial Year : {data.financialYear}</h1></div>
<div><h2>Quarter : {data.quarter}</h2></div>
</div>
<div>
<Tabs defaultActiveKey="Domain" id="uncontrolled-tab-example">
{posts}
</Tabs>
</div>
<button onClick={this.onSubmitRatings}> Submit </button>
</Auxilary>
);
}
}
export default QuarterLog;
I found a lot of solutions about this problem but none of them work.
I have a view which renders dynamically components depending on the backend response
/**
* Module dependencies
*/
const React = require('react');
const Head = require('react-declarative-head');
const MY_COMPONENTS = {
text: require('../components/fields/Description'),
initiatives: require('../components/fields/Dropdown'),
vuln: require('../components/fields/Dropdown'),
severities: require('../components/fields/Dropdown'),
};
const request = restclient({
timeout: 5000,
baseURL: '/api',
});
const { DropdownItem } = Dropdown;
class CreateView extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
states: props.states,
error: props.error,
spinner: true,
state: props.state,
prevState: '',
components: [],
};
this.handleChange = this.handleChange.bind(this);
this.getRequiredFields = this.getRequiredFields.bind(this);
this.onChangeHandler = this.onChangeHandler.bind(this);
this.changeState = this.changeState.bind(this);
this.loadComponents = this.loadComponents.bind(this);
}
componentDidMount() {
this.loadComponents();
}
onChangeHandler(event, value) {
this.setState((prevState) => {
prevState.prevState = prevState.state;
prevState.state = value;
prevState.spinner = true;
return prevState;
}, () => {
this.getRequiredFields();
});
}
getRequiredFields() {
request.get('/transitions/fields', {
params: {
to: this.state.state,
from: this.state.prevState,
},
})
.then((response) => {
const pComponents = this.state.components.map(c => Object.assign({}, c));
pComponents.forEach((c) => {
c.field.required = 0;
c.field.show = false;
});
response.data.forEach((r) => {
const ob = pComponents.find(c => c.field.name === r.name);
if (ob) {
ob.field.required = r.required;
ob.field.show = true;
}
});
this.setState({
components: pComponents,
fields: response.data,
spinner: false,
});
})
.catch(err => err);
}
loadComponents() {
this.setState((prevState) => {
prevState.components = Object.keys(MY_COMPONENTS).map((k) => {
const field = {
name: k,
required: 0,
show: true,
};
return {
field, component: MY_COMPONENTS[k],
};
});
return prevState;
});
}
handleChange(field, value) {
this.setState((prevState) => {
prevState[field] = value;
return prevState;
});
}
changeState(field, value) {
this.setState((prevState) => {
prevState[`${field}`] = value;
return prevState;
});
}
render() {
const Components = this.state.components;
return (
<Page name="CI" state={this.props} Components={Components}>
<Script src="vendor.js" />
<Card className="">
<div className="">
<div className="">
<Spinner
show={this.state.spinner}
/>
{Components.map((component, i) => {
const Comp = component.component;
return (<Comp
key={i}
value={this.state[component.field.name]}
field={component.field}
handleChange={this.handleChange}
modal={this.state.modal}
changeState={this.changeState}
/>);
})
}
</div>
</div>
</div>
</Card>
</Page>
);
}
}
module.exports = CreateView;
and the dropdown component
const React = require('react');
const request = restclient({
timeout: 5000,
baseURL: '/api',
});
const { DropdownItem } = Dropdown;
class DrpDwn extends React.Component {
constructor(props) {
super(props);
this.state = {
field: props.field,
values: [],
};
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('state', this.state.field);
console.log('prevState', prevState.field);
console.log('prevProps', prevProps.field);
console.log('props', this.props.field);
}
render() {
const { show } = this.props.field;
return (show && (
<div className="">
<Dropdown
className=""
onChange={(e, v) => this.props.handleChange(this.props.field.name, v)}
label={this.state.field.name.replace(/^./,
str => str.toUpperCase())}
name={this.state.field.name}
type="form"
value={this.props.value}
width={100}
position
>
{this.state.values.map(value => (<DropdownItem
key={value.id}
value={value.name}
primary={value.name.replace(/^./, str => str.toUpperCase())}
/>))
}
</Dropdown>
</div>
));
}
module.exports = DrpDwn;
The code actually works, it hide or show the components correctly but the thing is that i can't do anything inside componentdidupdate because the prevProps prevState and props are always the same.
I think the problem is that I'm mutating always the same object, but I could not find the way to do it.
What I have to do there is to fill the dropdown item.
Ps: The "real" code works, i adapt it in order to post it here.
React state is supposed to be immutable. Since you're mutating state, you break the ability to tell whether the state has changed. In particular, i think this is the main spot causing your problem:
this.setState((prevState) => {
prevState.components = Object.keys(MY_COMPONENTS).map((k) => {
const field = {
name: k,
required: 0,
show: true,
}; return {
field, component: MY_COMPONENTS[k],
};
});
return prevState;
});
You mutate the previous states to changes its components property. Instead, create a new state:
this.setState(prevState => {
const components = Object.keys(MY_COMPONENTS).map((k) => {
const field = {
name: k,
required: 0,
show: true,
};
return {
field, component: MY_COMPONENTS[k],
};
});
return { components }
}
You have an additional place where you're mutating state. I don't know if it's causing your particular problem, but it's worth mentioning anyway:
const pComponents = [].concat(this.state.components);
// const pComponents = [...this.state.components];
pComponents.forEach((c) => {
c.field.required = 0;
c.field.show = false;
});
response.data.forEach((r) => {
const ob = pComponents.find(c => c.field.name === r.name);
if (ob) {
ob.field.required = r.required;
ob.field.show = true;
}
});
You do at make a copy of state.components, but this will only be a shallow copy. The array is a new array, but the objects inside the array are the old objects. So when you set ob.field.required, you are mutating the old state as well as the new.
If you want to change properties in the objects, you need to copy those objects at every level you're making a change. The spread syntax is usually the most succinct way to do this:
let pComponents = this.state.components.map(c => {
return {
...c,
field: {
...c.field,
required: 0,
show: false
}
}
});
response.data.forEach(r => {
const ob = pComponents.find(c => c.field.name === r.name);
if (ob) {
// Here it's ok to mutate, but only because i already did the copying in the code above
ob.field.required = r.required;
ob.field.show = true;
}
})
I have following data structure:
const library = [
{
procedureName:'Build Foundations',
id:1,
tasks:[
{
taskName:'dig at least 1m deep', isCompleted:false, procedureId:1
},
{
taskName:'At least 50m wide digging', isCompleted:false, procedureId:1
},
{
taskName:'Buy megazords', isCompleted:false, procedureId:1
}
]
},
{
procedureName:'Building the roof',
id:2,
tasks:[
{
taskName:'Constructed according to the project', isCompleted:false, procedureId:2
},
{
taskName:'Protect wood from humidity', isCompleted:false, procedureId:2
},
{
taskName:'Roof build with the correct angle', isCompleted:false, procedureId:2
}
]
}
]
I want my function onTaskToggle to setState of library.tasks.isCompleted like this: library.tasks.isCompleted: !library.tasks.isCompleted
(I want to be able to distinguish the click of certain item as well)
I have no idea how get to this state.
Should I build my data structure differently or is there something I am missing?
onTaskToggle = (task) => {
const findProcedure = library => library.filter(procedure => procedure.id === task.procedureId);
const findTask = tasks => tasks.filter(singleTask => singleTask.taskName === task.taskName);
const toggledTask = findTask(findProcedure(this.state.library)[0].tasks);
const procedureIndex = this.state.library.indexOf(findProcedure(this.state.library)[0]);
const toggledTaskIndex = this.state.library[procedureIndex].tasks.indexOf(toggledTask[0]);
const insideProcedure = this.state.library[procedureIndex];
const insideTask = this.state.library[procedureIndex].tasks.indexOf(toggledTask[0]);
this.setState({
// Stuck there ...library
})
}
I'm not sure if it is needed, but that's the way I'm passing the data:
// Main component
class App extends Component {
constructor(props){
super(props);
this.state = {
projects,
library,
}
}
onTaskToggle = (task) => {
const findProcedure = library => library.filter(procedure => procedure.id === task.procedureId);
const findTask = tasks => tasks.filter(singleTask => singleTask.taskName === task.taskName);
const toggledTask = findTask(findProcedure(this.state.library)[0].tasks);
const procedureIndex = this.state.library.indexOf(findProcedure(this.state.library)[0]);
const toggledTaskIndex = this.state.library[procedureIndex].tasks.indexOf(toggledTask[0]);
const insideProcedure = this.state.library[procedureIndex];
const insideTask = this.state.library[procedureIndex].tasks.indexOf(toggledTask[0]);
this.setState({
// ...library
})
}
render() {
const {projects, library} = this.state;
return (
<div>
<Procedures library={library} onTaskToggle={this.onTaskToggle}/>
</div>
);
}
}
export default class Procedures extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
{
<ProceduresList library={this.props.library} onTaskToggle={this.props.onTaskToggle}/>
}
</div>
);
}
}
//Mapping through procedures
const ProceduresList = props => {
const list = props.library.map(procedure => {
const { id, procedureName, tasks } = procedure;
return(
<div key={id}>
<h4>{procedureName} </h4>
<div><ListingTasks tasks={tasks} onTaskToggle={props.onTaskToggle}/></div>
</div>
)
})
return <div>{list}</div>
}
export default ProceduresList;
// Last Component where all the magic shall happen
const ListingTasks = (props) => {
const list = props.tasks.map(task => { // under this line goes mapping through procedures
const taskName = task.taskName;
return <div key={taskName} onClick={() => props.onTaskToggle(task)}>{taskName}</div>
})
return <div>{list}</div>
}
export default ListingTasks
onTaskToggle(task) {
const { library } = this.state
const procedure = Object.assign({}, library.find(proc => (proc.id === task.procedureId)))
const procedureIndex = library.findIndex(prec => prec.id === procedure.id)
procedure.tasks = procedure.tasks.map((tsk) => {
if (tsk.taskName === task.taskName) {
tsk.isCompleted = !tsk.isCompleted
}
return tsk;
})
library.splice(procedureIndex, 1, procedure)
this.setState({ library })
}