I have an array of objects inside my class that I am modifying and only when a keypress happens do I want to render this object visually.
class Example extends React.Component {
constructor(props) {
super(props);
this.myArr = []; // this is an array of objects
}
render() {
return (
???
);
}
}
Now I modify the contents of this.myArr in many different methods. And only when I'm ready (on a keypress or some other event) do I want to render it.
Now in my render() should I have a reference to this.myArr and then use this.forceUpdate() when I want to force a re-render.
Or should I move myArr into this.state.myArr, and modify this.state.myArr in my methods and when I am ready to display it, in my render() reference to this.state.myArr, and somehow force a rerender with this.setState(myArr: this.state.myArr);
***Second Update - I think this may be what you want. Obviously, you'll need to add a lot of logic for your mouse click events. It should point you in the right direction though.
class Example extends React.Component {
constructor(props) {
super(props);
this.myArr = [];
this.state = {
myArr: [{ width: 10 }, { width: 20 }], // header widths
};
}
// call changeHeaders when needed
// it will update state, which will cause a re-render
changeHeaders = (column, newWidth) => {
const newArr = [...this.state.myArr];
if (newArr[column]) {
newArr[column].width = newWidth;
}
this.setState({ myArr: newArr });
}
renderArray = () => {
return this.state.myArr.map(({ width }) => <div>{width}</div>);
}
render() {
return (
<div>
{this.renderArray()}
</div>
);
}
}
Either way would work but I think its better practice to use this.state to hold your array and use this.setState() to force a re-render and call the this.setState within your keypress event callback
Here is how you update your array value -
Correct modification of state arrays in ReactJS
There's a very good explanation why you should avoid using this.forceUpdate() in here answered by Chris.
In this case you should use state. State is intended to be used for any data that affect how your component looks. Remember that you may not modify your state directly, it's an antipattern. Instead, you should create a copy of the array and update the state with that modified copy. Like so:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {myArr: []}; // this is an array of objects
}
mutateArraySomehow() {
const nextArr = [...this.state.myArr];
nextArr.push('heyyoooo');
this.setState({myArr: nextArr});
}
render() {
return (
???
);
}
}
This is how i would have done it
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
myArr: [],
display: false
}
}
render() {
if(this.state.display) {
return (
<MyComponent onKeyPress=(ev=>{
this.setState({display: true})
})
/>
);
} else {
return (<div></div>)
}
}
}
When there is a modification to the array elements, you need to do a setState of status to true.This would perform the conditional rendering and will display the modified array.
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
myArr = []; // this is an array of objects
status:false
}
}
modifyArr = () => {
//some array modifications
this.setState({status:true})
}
render() {
return (
{this.state.status ? null : (<div>`Display your array here`</div>)}
);
}
}
In short, define state inside class like:
state: {
firstName: String
} = {
firstName: ''
}
And inside render function you would do this:
this.setState({ firstName: 'Junior' })
Related
i have a select menu with defaultValue is null
when i pass props to it , it dosent rerender with the new props as defaultValues
ps : the select is multi
i tried to use component will recieve props and everything that i find but still dosent work
this is my select component :
import React, { useState, useEffect } from "react";
import Select from "react-select";
class SelectMenu extends React.Component {
state = {
defaultValues: [],
};
componentWillReceiveProps(newProps) {
this.setState({ defaultValues: newProps.defaultValue });
}
render() {
return (
<Select
options={this.props.options}
closeMenuOnSelect={this.props.closeMenuOnSelect}
components={this.props.components}
isMulti={this.props.isMulti}
onChange={(e) => this.props.onChange(e, this.props.nameOnState)}
placeholder={this.props.default}
defaultValue={this.state.defaultValues}
/>
);
}
}
export default SelectMenu;
componentWillReceiveProps won't be called during mounting.
React doesn’t call UNSAFE_componentWillReceiveProps() with initial props during mounting. It only calls this method if some of component’s props may update. (https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops)
Also, componentWillReceiveProps is deprecated and will be removed in React 17. Take a look at getDerivedStateFromProps instead, and especially the notes on when you do not need it.
I beleive that in your case using the constructor will be perfectly fine, something like:
class Components extends React.Component {
constructor(props) {
super(props)
this.state = { some_property: props.defaultValue }
}
}
i find a solution for this problem
by using components will recieve props
and setting my state with the comming props
and in the render you need to do condition to render the select menu only if the state.length !== 0
i posted this answer just in case someone face the same problem i know its not the most optimal solution but it works for me
sorry for the previous solution but its not optimal i find a way to make it work
so instead of defaultvalues
you have to make its as value props
and if you want to catch the deleted and added values to your default
this function will help you alot
onChange = (e) => {
if (e === null) {
e = [];
}
this.setState({
equipments: e,
});
let added = e.filter((elm) => !this.state.equipments.includes(elm));
if (added[0]) {
let data = this.state.deletedEquipments.filter(
(elm) => elm !== added[0].label
);
this.setState({
deletedEquipments: data,
});
}
let Equipments = e.map((elm) => elm.label);
let newEquipments = Equipments.filter(
(elm) => !this.state.fixed.includes(elm)
);
this.setState({
newEquipments: newEquipments,
});
let difference = this.state.equipments.filter((elm) => !e.includes(elm));
if (difference.length !== 0) {
if (
!this.state.deletedEquipments.includes(difference[0].label) &&
this.state.fixed.includes(difference[0].label)
) {
this.setState({
deletedEquipments: [
...this.state.deletedEquipments,
difference[0].label,
],
});
}
}
};
constructor(props) {
super(props);
this.state = {
equipments: [],
newEquipments: [],
deletedEquipments: [],
};
}
I'm running into a recurring issue in my code where I want to grab multiple pieces of data from a component to set as states, and push those into an array which is having its own state updated. The way I am doing it currently isn't working and I think it's because I do not understand the order of the way things happen in js and react.
Here's an example of something I'm doing that doesn't work: jsfiddle here or code below.
import React, {Component} from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
categoryTitle: null,
categorySubtitle: null,
categoryArray: [],
}
}
pushToCategoryArray = () => {
this.state.categoryArray.push({
'categoryTitle': this.state.categoryTitle,
'categorySubtitle': this.state.categorySubtitle,
})
}
setCategoryStates = (categoryTitle, categorySubtitle) => {
this.setState({
categoryTitle: categoryTitle,
categorySubtitle: categorySubtitle,
})
this.pushToCategoryArray();
}
render() {
return (
<CategoryComponent
setCategoryStates={this.setCategoryStates}
categoryTitle={'Category Title Text'}
categorySubtitle={'Category Subtitle Text'}
/>
);
}
}
class CategoryComponent extends Component {
render() {
var categoryTitle = this.props.categoryTitle;
var categorySubtitle = this.props.categorySubtitle;
return (
<div onClick={() => (this.props.setCategoryStates(
categoryTitle,
categorySubtitle,
))}
>
<h1>{categoryTitle}</h1>
<h2>{categorySubtitle}</h2>
</div>
);
}
}
I can see in the console that I am grabbing the categoryTitle and categorySubtitle that I want, but they get pushed as null into this.state.categoryArray. Is this a scenario where I need to be using promises? Taking another approach?
This occurs because setState is asynchronous (https://reactjs.org/docs/state-and-lifecycle.html#using-state-correctly).
Here's the problem
//State has categoryTitle as null and categorySubtitle as null.
this.state = {
categoryTitle: null,
categorySubtitle: null,
categoryArray: [],
}
//This gets the correct values in the parameters
setCategoryStates = (categoryTitle, categorySubtitle) => {
//This is correct, you're setting state BUT this is not sync
this.setState({
categoryTitle: categoryTitle,
categorySubtitle: categorySubtitle,
})
this.pushToCategoryArray();
}
//This method is using the state, which as can be seen from the constructor is null and hence you're pushing null into your array.
pushToCategoryArray = () => {
this.state.categoryArray.push({
'categoryTitle': this.state.categoryTitle,
'categorySubtitle': this.state.categorySubtitle,
})
}
Solution to your problem: pass callback to setState
setCategoryStates = (categoryTitle, categorySubtitle) => {
//This is correct, you're setting state BUT this is not sync
this.setState({
categoryTitle: categoryTitle,
categorySubtitle: categorySubtitle,
}, () => {
/*
Add state to the array
This callback will be called once the async state update has succeeded
So accessing state in this variable will be correct.
*/
this.pushToCategoryArray()
})
}
and change
pushToCategoryArray = () => {
//You don't need state, you can simply make these regular JavaScript variables
this.categoryArray.push({
'categoryTitle': this.state.categoryTitle,
'categorySubtitle': this.state.categorySubtitle,
})
}
I think React doesn't re-render because of the pushToCategoryArray that directly change state. Need to assign new array in this.setState function.
// this.state.categoryArray.push({...})
const prevCategoryArray = this.state.categoryArray
this.setState({
categoryArray: [ newObject, ...prevCategoryArray],
)}
Newbie question.
I have an array that i need to add to and I am using slice to do this. I am using gatsby/react. The problem I have is each time my page/component rerenders the object I am adding to my array gets added again
Here is my code
class IndexPage extends PureComponent {
render() {
const data = this.props.data;
const hostels = data.featuredHostel.edges;
const hopimage = data.hop.childImageSharp.fluid;
hostels.splice(8, 0, {
node: {
featuredImage: {
alt: 'Bedhopper Image',
fluid: hopimage
},
id: 'bedhopper',
slug: '/deals/bed-hopper',
title: 'For travel adicts who want to stay everywhere'
}
});
return (....
Been stuck on this for a while now. Any help appreciated
You should make any calculation on constructor or componentDidMount.
class IndexPage extends PureComponent {
constructor(props) {
super(props);
this.state = {
hostels: props.data.featuredHostel.edges.concat(...)
}
}
componentDidMount() {
}
render() {
const { hostels } = this.state;
return (
...
)
Probably, your case can works too (I didn't see whole code). I guess you use array index as key for render
hostels.map((hostel, hostelIndex) => (<SomeComponent key={hostelIndex} />))
You can change key to hostel.id for example for more unique block.
I wonder how should I approach properties in component's state that depend on another property, for example:
class Sample extends Component {
constructor(props) {
super(props);
this.state = {
mainProperty: 1,
something: {
dependingOnMainProperty: `foo${this.state.mainProperty}bar`
}
}
}
}
Basically, I would like dependingOnMainProperty to be automatically updated when I change mainProperty through setState. Should I write a function for that or is there some other solution?
You may do with getters and setters as follows;
class Component {
constructor(...props){
props.forEach(prop => this[prop] = "whatever");
}
}
class Sample extends Component {
constructor(...props) {
super(...props);
this.state = {
something: {
__mp__: "tester",
dependingOnMainProperty: `foo${this.mainProperty}bar`,
get mainProperty(){
return this.__mp__;
},
set mainProperty(v){
this.__mp__ = v;
this.dependingOnMainProperty = `foo ${v} bar`;
}
}
};
}
}
var s = new Sample("a", "b");
s.state.something.mainProperty = "hello";
console.log(s.state.something.dependingOnMainProperty);
https://jsfiddle.net/69z2wepo/81913/
I am decorating a component tree and adding some meta data to my components. Works wonderfully at the top level component (A); but if I try and decorate my sub components (commented out but un-commenting illustrates the issue) - the render chain breaks and props that get passed down do not render properly (or at all). Does anyone have any insight - I have attached a fiddle above.
var dec = (t, k, d) => {
console.log('hello decoration')
var el = React.cloneElement(d.value(), {'label': 'my-component-label'})
return {value: () => el}
}
class B extends React.Component{
constructor(props) {
super(props)
}
//#dec
render() {
return <div>
{this.props.data}
</div>
}
}
class A extends React.Component{
constructor(props) {
super(props)
}
#dec
render() {
return <div>
<B data={99 + 101}/>
</div>
}
}
ReactDOM.render(
<A/>,
document.getElementById('container')
);
In order to understand recursion, you must first understand recursion!
That aside, I've used this snippet successfully in the past :
recursiveCloneChildren(children) {
return React.Children.map(children, (child) => {
let childProps = {};
if (!child || !child.props) {
return child;
}
childProps.DECORATED = true;
childProps.children = this.recursiveCloneChildren(child.props.children);
return React.cloneElement(child, childProps);
});
}
Just give it the this.props.children of a Component and it'll do the rest. in this snippet we simply add a DECORATED boolean value to all children.