How to add new key and vaule from object inside Array? - arrays

I am using ReactJSand Redux using the code below show how I am tring to add a new key:value in each object. Actually the error are:
Console log: Uncaught (in promise) TypeError: Cannot read property '0' of undefined
Web: TypeError: Cannot read property '0' of undefined
The data received from the API is:
The complete code is:
import React from "react";
import axios from "axios";
import TableData from "../TableData";
import CustomForm from "../FormCliente";
import Modal from "../Modal";
//Función que conecta un componente a Redux store.
import { connect } from "react-redux";
import { createBrowserHistory } from 'history';
class ClienteList extends React.Component {
state = {
DataFromApi: []
};
fetchArticles = () => {
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.headers = {
"Content-Type": "application/json",
Authorization: `Token ${this.props.token}`,
};
axios.get("http://192.168.196.49:8000/clientes/api/").then(res => {
this.setState({
DataFromApi: res.data
});
});
}
componentDidMount() {
this.fetchArticles();
}
componentWillReceiveProps(newProps) {
if (newProps.token) {
this.fetchArticles();
}
}
render() {
console.log("Token_desde_connect:", this.props.token);
const history = createBrowserHistory();
const location = history.location;
console.log("debug_1.1: ", location)
const dummy = event => {
console.log('mostrando dummy:', event.target.id);
}
const encabezado = [
{
label: 'Cliente',
field: 'nombre',
sort: 'asc',
width: 150
},
{
label: 'Fecha de alta',
field: 'fecha_alta',
sort: 'asc',
width: 270
},
{
label: 'Usuario ID',
field: 'usuario_id',
sort: 'asc',
width: 270
},
{
label: 'Herramientas',
field: '',
sort: 'asc',
width: 270
}
];
console.log("#-Before-#Data_from_API: ", this.state.DataFromApi);
    // Modificar el array con los datos obtenidos de la API.
    //for (let x in this.state.DataFromApi){
//console.log("#-valor_x:", x, "-#Data_from_API: ", this.state.DataFromApi[x]);
      //this.setState.DataFromApi[x] = Object.assign({ coco: x },this.stateDataFromApi[x] ) ;
      //console.log("#-Inside FOR -#New_Data_from_API: ", this.state.DataFromApi[x] );
//Otra forma de hacer:
//console.log("#-Before-#Data_from_API: ", this.state.DataFromApi);
// Modificar el array con los datos obtenidos de la API.
//let data=this.state.DataFromApi;
//data.forEach( (obj) => obj.city="Spain");
//this.setState({ DataFromApi:data})
    //};
//console.log("#Al finalizar el FOR_ fuera -#New_Data_from_API: ", this.state.DataFromApi );
//this.setState((prevState) => {
// DataFromApi: prevState.DataFromApi.map((obj) => ({
// ...obj,//<- This line will copy the entire object
// myKey: obj.myValue,//<- And this will add/override the property in this obj
// })
//);
this.setState((prevState) => ({
DataFromApi: prevState.DataFromApi.map((x) => ({
...x,//<- This line will copy the entire object
coco: x,//<- And this will add/override the property in this obj
}))
}));
console.log("#-After-#Data_from_API: ", this.state.DataFromApi);
return (
<div>
<Modal requestType="post" btnText="Guardar"/>
<TableData data={this.state.DataFromApi} Encabezado={encabezado}/> <br />
<h2> Create an article </h2>
<CustomForm requestType="post" itemID={null} btnText="Create" />
<button id="dummy" onClick={dummy}>Dummy button</button>
</div>
);
}
}
const mapStateToProps = state => {
return {
token: state.token
};
};
export default connect(mapStateToProps)(ClienteList);
ERROR:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

First of all, having your setState inside a loop is not a good idea, what you want to do in this case is pass a function to setState and that function will receive the "previous" (current) state.
this.setState((prevState) => ({
DataFromApi: prevState.DataFromApi.map((x) => ({
...x,//<- This line will copy the entire object
coco: x,//<- And this will add/override the property in this obj
}))
}));
It's also recommended to use this method (passing a function) when you're trying to update your state, based on the previous state, like this case, you're modifying your array, this is because this.setState is asynchronous and you can run into some problems if you don't use the function approach under this circumstances.
I hope this helps.
Update: Missing parenthesis, I used my editor this time so this works, I apologize about that.

Related

How to get state variable names from names(string) value in React Native?

1. I set name in statesCorelatedFields and setStatesCorelatedFields inside below codes,
how can I get state and setState variables from there? (please see below example)
2. Does my below approach right?
3. Any suggestion will be highly appreciated.
I am using react native 0.68.5.
Previously, I used class component, now I am migrating to function component.
I have a reuseable file and App file like below:
reuseable.js
// import ...
export const handleFocus = (
state,
setState,
focusStyle,
// array of state variables of corelated fields
statesCorelatedFields,
// array of setState methods of corelated fields
setStatesCorelatedFields,
// blur style if no text value
blurNoTextStyle,
) => {
const stateData = { ...state };
stateData.styleName = { ...focusStyle };
// for corelated fields: empty value and set blurNoTextStyle
if (statesCorelatedFields.length) {
let stateCorelatedFieldData;
for (i = 0; i < statesCorelatedFields.length; i++) {
stateCorelatedFieldData = { ...statesCorelatedFields[i] };
stateCorelatedFieldData.value = '';
stateCorelatedFieldData.styleName = { ...blurNoTextStyle };
setStatesCorelatedFields[i](stateCorelatedFieldData);
}
}
setState(stateData);
};
// export const handleChangeText=(state, setState, text, ...)=>{...}
// export const handleBlur=(state, setState, ...)=>{...}
// ...
App.js
// import ...
// import all methods from reuseable.js
const App = () => {
const [email, setEmail] = useState({
name: 'email',
value: '',
styleName: { ...styles.blurNoTextStyle },
error: '',
statesCorelatedFields: [],
setStatesCorelatedFields: [],
});
const [countryCode, setCountryCode] = useState({
name: 'countryCode',
value: '',
styleName: { ...styles.blurNoTextStyle },
error: '',
// I set name here; how can I get state and setState variable from here
statesCorelatedFields: ['phoneNumber'],
setStatesCorelatedFields: ['setPhoneNumber'],
});
const [phoneNumber, setPhoneNumber] = useState({
name: 'phoneNumber',
value: '',
styleName: { ...styles.blurNoTextStyle },
error: '',
statesCorelatedFields: [],
setStatesCorelatedFields: [],
});
return (
<>
{/* components */}
<TextInput
value={countryCode.value}
onChangeText={(text) => handleChangeText(countryCode, setCountryCode, text)}
onFocus={() => handleFocus(countryCode, setCountryCode, styles.focusStyle, countryCode.statesCorelatedFields, countryCode.setStatesCorelatedFields)}
onBlur={() => handleBlur(countryCode, setCountryCode)}
/>
{/* other components */}
</>
);
}
const styles = StyleSheet.create({
// styles goes here
});
export default App;
Thanks in advance.
Moves this setStatesCorelatedFields out of the loop body you are updating the state on every iteration which doesn't need. It causes to slow down your component
you can do like this:
if (statesCorelatedFields.length) {
let stateCorelatedFieldData;
for (i = 0; i < statesCorelatedFields.length; i++) {
stateCorelatedFieldData = { ...statesCorelatedFields[i] };
stateCorelatedFieldData.value = "";
stateCorelatedFieldData.styleName = { ...blurNoTextStyle };
}
setStatesCorelatedFields[i](stateCorelatedFieldData);
}

How can i make a button actvate my searchSpell function?

i am finishing my first react project. But I cant make this button work. I keep giving me the same error below
error console message: Uncaught TypeError_axios_api_js__WEBPACK_IMPORTED_MODULE_1__.default.request is not a function
code:
function Header(){
const { searchSpell } = Hooker();
const [whatSpell, setWhatSpell] = useState()
function pesquisar () {
if(!whatSpell){
alert("escreva alguma coisa antes de pesquisar")
return
} else{
return searchSpell(whatSpell)
}
}
another code:
import { SpellContext } from "../provider.js"
function Hooker (){
const { spellState, searchSpell } = useContext(SpellContext)
return { spellState, searchSpell }
}
export default Hooker
and the function one:
function BaseInfo({ children }){
const [spellState, setSpellState] = useState({
name: undefined,
desc: undefined,
higher_level: undefined,
range: undefined,
components: undefined,
material: undefined,
ritual: false,
duration: undefined,
concentration: false,
casting_time: undefined,
level: 0,
});
const searchSpell = (spellName) => {
api.request(spellName).then(function(response) {
setSpellState(prevState => ({
...prevState,
name: response.data.name,
desc: response.data.desc,
higher_level: response.data.higher_level,
range: response.data.range,
components: response.data.components,
material: response.data.material,
ritual: response.data.ritual,
duration: response.data.duration,
concentration: response.data.concentration,
casting_time: response.data.duration,
level: response.data.duration,
})
)
})
console.log(spellState.duration)
}
const contextValue = {
spellState,
searchSpell: useCallback((spellName) => searchSpell(spellName), []),
}
return(
<SpellContext.Provider value={contextValue}>
{children}
</SpellContext.Provider>
)
}
export default BaseInfo
I don't think it's the button's fault. The error message indicates you're calling searchSpell, but there is something wrong with the way you're writing the API request.
If you're using axios as the error suggests. Check the axios documentation for proper installation, importing and usage.
Example usage for your use case:
import axios from "axios"
// ... ...
axios.request(spellName).then(function() {
// ... ...

FlatList in react native not rendering data from state

FlatList is not rendering data from state however it is working for the DATA variable. this.state.DATA is an array of objects just like the DATA variable.The DATA variable is just a dummy variable that was given in the reactnative docs. I want to display the contents of this.state.DATA.
import React, { Component } from 'react';
import { Text, View ,FlatList} from 'react-native';
import SectionHeader from '../SectionHeader';
import {TableHeader,TableHeaderText,IssueContainer} from './style';
import {CheckOutlined,InfoCircleOutlined,CaretDownOutlined} from '#ant-design/icons'
const DATA = [
{
id: '1',
title: "No show password eye button in Login form",
},
{
id: '2',
title: 'Second Item',
},
{
id: '3',
title: 'Third Item',
},
];
var repos=[],issues=[];
export default class App extends Component {
state={isLoading:true};
componentDidMount() {
fetch('https://api.github.com/orgs/anitab-org/repos')
.then((response)=>response.json())
.then((json)=> json.forEach(function(repo,idx){
repos.push(repo.name);
fetch('https://api.github.com/repos/anitab-org/'+repo.name+'/issues')
.then((response)=>response.json())
.then((json)=>json.forEach(function(issue,idx){
var flag=false;
var issue_tmp={
id:issue.id.toString(),
url:issue.html_url,
title:issue.title,
milestones:issue.milestones,
comments:issue.comments,
number:issue.number,
assignees:issue.assignees,
labels:[],
};
issue.labels.forEach(function(label){
if(label.name==="First Timers Only")
flag=true;
issue_tmp.labels.push({
id:label.id,
name:label.name,
color:label.color
})
})
if(flag===true && issue_tmp!=null)
issues.push(issue_tmp)
}));
}))
.then(()=>{
this.setState({
repos:repos,
DATA:issues,
isLoading:false,
});
})
}
render() {
if(this.state.isLoading===true)
return(<></>)
else{
return (
<View style={{alignItems: 'left',width:'80%'}}>
<SectionHeader title="SOME COOL FIRST-TIME ISSUES TO WORK ON"/>
<TableHeader>
<TableHeaderText style={{color:'#000',textAlign:'left'}}><InfoCircleOutlined /> 5 Open</TableHeaderText>
<Text style={{flex:6,color:'#586069'}}><CheckOutlined /> 45 Closed</Text>
<TableHeaderText>Author <CaretDownOutlined /></TableHeaderText>
<TableHeaderText>Label <CaretDownOutlined /></TableHeaderText>
<TableHeaderText>Milestone <CaretDownOutlined /></TableHeaderText>
<TableHeaderText>Assignee <CaretDownOutlined /></TableHeaderText>
<TableHeaderText>Sort <CaretDownOutlined /></TableHeaderText>
</TableHeader>
<FlatList
data={this.state.DATA}
renderItem={({item})=>(
<IssueContainer key={item.id}><Text>{item.title}</Text></IssueContainer>
)}
keyExtractor={item => item.id}
/>
</View>
);
}
}
};
The reason it doesn't work is because you have nested promises. The outer then won't wait the inner ones to execute the following code. This way last then with setState is executed without those promises being resolved:
.then((json)=> json.forEach(function(repo,idx){
// bunch of promises being executed here with some chained then blocks
// outer 'then' chain doesn't wait these promises to resolve
}))
.then(()=>{
// since the previous one doesn't wait its inner promises to execute
// this chained 'then' is executed without those promises return their values
this.setState({
repos:repos,
DATA:issues,
isLoading:false,
});
I rewrote your code with async/await because with some many promises it's a hard read. I use Promise.all to wrap all fetches. Also I abstracted your issue treatment to its own normalize function:
state = {
isLoading: true,
repos: [],
DATA: [],
};
async componentDidMount() {
const repos = [];
try {
const response = await fetch('https://api.github.com/orgs/anitab-org/repos');
const jsonData = await response.json();
const DATA = await Promise.all(jsonData.map(async ({ name }) => {
repos.push(name);
const issuesResponse = await fetch(`https://api.github.com/repos/anitab-org/${name}/issues`);
const issuesJSON = await issuesResponse.json();
const repoIssues = issuesJSON.map(this.normalizeIssue);
return repoIssues.filter(issue => issue !== undefined);
}))
// DATA needs to be flat since it's an array of arrays
this.setState({
repos,
DATA: DATA.flat(),
isLoading:false,
})
} catch (error) {
console.log(error);
}
}
normalizeIssue = (issue) => {
let flag = false;
const issueNormalized = {
id:issue.id.toString(),
url:issue.html_url,
title:issue.title,
milestones:issue.milestones,
comments:issue.comments,
number:issue.number,
assignees:issue.assignees,
labels:[],
};
issue.labels.forEach(function(label){
if(label.name === "First Timers Only") flag = true;
issueNormalized.labels.push({
id:label.id,
name:label.name,
color:label.color
})
})
if(flag === true && issueNormalized !== null) return issueNormalized
}

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array

I'm trying to convert a Class-based component to a Functional component. I get the above-mentioned error if I use the same code that was under componentDidMount in useEffect hook.
// Class based component
class Container extends Component {
state = {
elements: [],
counter: 1,
bgColor: "#ffffff",
botTextColor: "#000000",
botBGColor: "#aaaaaa",
userTextColor: "#000000",
userBgColor: "#aaaaaa",
};
componentDidMount = async () => {
this.setState({
bgColor: this.props.chat.currentChat.bgColor,
botTextColor: this.props.chat.currentChat.botTextColor,
botBGColor: this.props.chat.currentChat.botBGColor,
userTextColor: this.props.chat.currentChat.userTextColor,
userBgColor: this.props.chat.currentChat.userBgColor,
});
this.setState({
elements:
this.props.chat.currentChat.elements &&
this.props.chat.currentChat.elements.length > 0
? elements
: [
{
id: "0",
data: {
label: (
<WelcomeNode
id={"0"}
images={this.props.chat.media.map((e) => e.file)}
updateChoices={(choices) =>
this.updateChoices("0", choices)
}
updateMessage={(message) =>
this.updateMessage("0", message)
}
updateImage={(e) => this.updateImage(e, "0")}
addEdge={this.addEdgeCustom}
deleteEdgeChoice={(index) =>
this.deleteEdgeChoice("0", index)
}
isChoiceConnected={(index) =>
this.isChoiceConnected("0", index)
}
></WelcomeNode>
),
message: "",
choices: [],
type: "welcome",
id: "0",
},
className: "node-elements",
position: { x: 100, y: 100 },
},
],
counter: elements.length > 0 ? elements.length : 1,
});
}
}
The Following is the functional component where the error occurs
// Functional component
const initialState = {.....}
const Container = () => {
const [state, setState] = useState(initialState);
const { auth, chat } = useSelector((state) => ({ ...state }));
const dispatch = useDispatch();
const history = useHistory();
useEffect(() => {
setState({
...state,
bgColor: chat.currentChat.bgColor,
botTextColor: chat.currentChat.botTextColor,
botBGColor: chat.currentChat.botBGColor,
userTextColor: chat.currentChat.userTextColor,
userBgColor: chat.currentChat.userBgColor,
});
setState({
...state,
elements:
chat.currentChat.elements && chat.currentChat.elements.length > 0
? elements
: [
{
id: "0",
data: {
label: (
<WelcomeNode
id={"0"}
images={chat.media.map((e) => e.file)}
updateChoices={(choices) => updateChoices("0", choices)}
updateMessage={(message) => updateMessage("0", message)}
updateImage={(e) => updateImage(e, "0")}
addEdge={(e) => addEdgeCustom(e)}
deleteEdgeChoice={(index) =>
deleteEdgeChoice("0", index)
}
isChoiceConnected={(index) =>
isChoiceConnected("0", index)
}
></WelcomeNode>
),
message: "",
choices: [],
type: "welcome",
id: "0",
},
className: "node-elements",
position: { x: 100, y: 100 },
},
],
counter: elements.length > 0 ? elements.length : 1,
});
}, []);
}
The following error is thrown and the browser crashes Uncaught (in promise) Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
sorry but your code is too complicated to read please reorganize it to make it more readable and understandable. please try charging useSelector line to this line:
const { auth, chat } = useSelector((state) => state);
this is causing multi render because useSelector detect state is recreating(using spread operator) so it would rerender the component.
plus in useEffect when you are setting the state use setState callback, this will not override your previous state update :
setState(prev=>({...prev,newState}))
useEffect usually requires a dependency array. What you use inside of the useEffect hook should go into that array for example we have a function that sets the id. The useEffect dependency will want the id in the array. Thus only update/run this useEffect hook if the id changes.
useEffect(() => {
setId(id)
}, [id])
If you only want to run the useEffect once on first render you can leave the array blank like this:
useEffect(()=>{
//http fetch request or something
}, [])

React set state error outside of componentDidMount

Im getting an error Unhandled Rejection (Error): Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops. when I try to set state in my retrieveRoleMembers function not sure how to fix it, any feedback is appreciated!
class MainCard extends Component {
state = {
userResponseData:[] ,
roleResponseDataID:[]
}
handleChange = (tab) => {
window.alert(`Tab changed to: ${tab}`);
};
retrieveRoleMembers(){
var i,j;
for (i = 0; i < this.props.userRoleDataValue.length; i++) {
if(this.props.userRoleDataValue[i].role_id === this.state.roleResponseDataID.id){
for(j=0;j<this.state.userResponseData.length;j++){
if(this.props.userRoleDataValue[i].user_id === this.state.userResponseData[j].id)
{
this.setState({ // This is where the error is happening
outputRoleMembers: this.state.userResponseData[j],
})
}
}
}}}
componentDidMount() {
this.props.getComponentById(VIEW_ROLES, Roles, this.props.searchValue.value).then(() => {
return this.setState({
roleResponseDataID: this.props.roles.data,
cardHandle: false,
})
});
this.props.fetchComponent([IS_FETCHING_DBUSERS, FETCH_DBUSERS_SUCCESS], users)
.then(() => {
return this.setState({
userResponseData: this.props.users.data,
})
});
}
render() {
if (this.props.cardHandle) {
return null
}
else {
if (this.props.sendOptionSelected === 'Role') {
this.retrieveRoleMembers()
return (
<Card mr={'0px'}>
<Tabs defaultActiveTab="Members" onChange={this.handleChange} >
{/* Group of tabs */}
<Tabs.Tab label="Members">Members</Tabs.Tab>
<Tabs.Tab label="Access">Access</Tabs.Tab>
{/* Tab panels */}
<Tabs.Panel label="Members">
<Table
data={Array.isArray(this.state.outputRoleMembers) ? this.state.outputRoleMembers : [this.state.outputRoleMembers]}
defaultPageSize={[this.state.outputRoleMembers].length}
columns={
[
{
Header: 'Fisrt Name',
accessor: 'first_name'
},
{
Header: 'Last Name',
accessor: 'last_name'
}
]
}
sortable={false}
resizable={false}
showPagination={false}
onSortedChange={() => { }}
/>
</Tabs.Panel>
</Tabs>
</Card>
)
}
}
}
const mapStateToProps = state => {
return {
roles: state.roles.item,
users: state.users
}
}
export default connect(mapStateToProps, { getComponentById,fetchComponent })(MainCard);
and when I change retrieveRoleMembers to look like so, my code works but when I inspect the console log I see a infinite loop / renders for VIEW_DBUSERS
retrieveRoleMembers(){
var i;
for (i = 0; i < this.props.userRoleDataValue.length; i++) {
if(this.props.userRoleDataValue[i].role_id === this.state.roleResponseDataID.id){
this.props.getComponentById(VIEW_DBUSERS, users, this.props.userRoleDataValue[i].user_id).then(() => {
return this.setState({
outputRoleMembers: this.props.users.data,
})
});
}}}
The problem is you are calling function inside render method. That sets the State and calls the render method again and so on. So it created a loop.
Hence you get
Unhandled Rejection (Error): Maximum update depth exceeded
I put everything inside componentDidMount by making an async function.
componentDidMount() {
this.preFetchData();
}
preFetchData async () { // made this async function.. using await to make code sync
await this.props.getComponentById(VIEW_ROLES, Roles, this.props.searchValue.value);
await this.props.fetchComponent([IS_FETCHING_DBUSERS, FETCH_DBUSERS_SUCCESS], users);
this.setState({ roleResponseDataID: this.props.roles.data, cardHandle: false, userResponseData: this.props.users.data }, () => {
this.retrieveRoleMembers(); // call your method here
});
}

Resources