Should I make all class variables part of state? - reactjs

Say I have a class variable which I know won't change but is still needed to render the component. Something like this perhaps:
class MyComponent extends React.Component {
constructor(props) {
super(props);
let { arrayOfImages } = this.props;
this.arrayOfImages = arrayOfImages;
this.state = {
colorOfBackground: 'blue'
};
}
render() {
let images = this.arrayOfImages.map(image => {
return <img src={image.src} />
});
return (
<div style={{color: this.state.colorOfBackground}}>
{images}
</div>
);
}
}
Is it better to just make the arrayOfImages part of the state or just keep it as a class variable?

No, It is not necessary to put arrayOfImages in state if this will not change. It is better to put those value in state which can be change and want to re-execute code every time or conditionally when it change.

Related

How can I pass state to grandparent so that uncle/aunt can use it?

I've been struggling for hours trying to get some code to work. I'm new with React, but I have spent a lot time looking for a solution to this as well, and updating this code as I understood with no success.
Basically my app is a component that splits into two components, with one of those splitting into 9 buttons. When I click one of those buttons, I want its uncle/aunt to recognize that, and use the id of the button that was pushed to create a message.
I figured I should be passing the button id up to the grandparent so that it can pass the id down to the uncle/aunt. But its the passing the id to the grandparent I'm struggling with.
This is the general set up below:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
"x" : " "
};
getX(x){
this.setState({"x": x})
}
render() {
return (
<div>
<A getX={this.getX}/>
<B x={this.state.x} />
</div>
)
}
}
const A = (props) => {
const getX = (x) => props.getX(x);
a = [];
for (let i=0; i<9; i++) {
a.push(<C id={i} getX={getX}/>);
return <div>{a}</div>
}
class C extends React.Component {
constructor(props) {
super(props);
this.state = {
"id" : props.id,
"getX" : (x) => props.getX(x)
}
this.handleMouseDown = this.handleMouseDown.bind(this);
}
handleMouseDown(e) {
this.state.getX(e.target.id);
}
render() {
<div />
}
}
class B extends React.Component {
constructor(props){
super(props);
this.state = {
"x" : props.x
}
}
render() {
return <div>{this.state.x}</div>
}
}
Firstly, the getX() method of the App component doesn't seem to be working how I expected it to. By that I mean, when I add getX("7"); to the render method of the App component, just before the return statement, the whole thing crashes. But if I replace this.setState({"x": x}) with this.state.x = x in the getX() method, then the state sucessfully passes down to the component B, which is something at least. But, I don't understand why.
Secondly, I can't work out how to modify the App component's state from within component A. The getX() method of the App component doesn't seem to be passed into component A as I expected. Again, if I insert getX("7"); before the return statement of component A, the whole thing crashes again. I expected the getX function of component A to be the same function as the getX method of the App component, and therefore update the state of the App component. But I've had no success with that at all. I've even tried inserting this.getX = this.getX.bind(this) into the constructor of the App component, but that didn't solve everything for me.
Lastly, as you can probably guess, I cant modify the App component's state from any of the C components.
Any ideas? I'm stumped.
I have modified your example so that it works. A few things:
Dont copy props to state, that is an antipattern and creates bugs (as you have seen). Dont copy the id or the function passed from component A to component C, or in component B. Just use the props values.
You had some syntax errors that I fixed.
You didnt return the array created in component A.
(This is my preference, but I will argue that you are setting a value, not getting, so i renamed getX to setX.)
There was nothing returned from component C. I was not sure what you was suppoosed to be returning from that component, so I just created a button with a click-handler.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
x: '',
};
this.setX = this.setX.bind(this);
}
setX(x) {
this.setState({ x: x });
}
render() {
return (
<div>
<A setX={this.setX} />
<B x={this.state.x} />
</div>
);
}
}
const A = (props) => {
let a = [];
for (let i = 0; i < 9; i++) {
a.push(<C id={i} setX={props.setX} />);
}
return <div>{a}</div>;
};
class B extends React.Component {
render() {
return <div>{this.props.x}</div>;
}
}
class C extends React.Component {
constructor() {
super();
this.handleMouseDown = this.handleMouseDown.bind(this);
}
handleMouseDown() {
this.props.setX(this.props.id);
}
render() {
return <button onClick={this.handleMouseDown}>Click me</button>;
}
}

How do I successfully change the state in one component from another?

I am calling setState in one component in an onClick function in another component but it says this.setState is not a function and that a component is repeatedly calling setState inside componentWill update. How do i fix this?
This is the relevant of the app.js file:
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activeScreen: 'dashboard'
}
}
setActiveScreen(key) {
this.setState({
activeScreen: key
})
}
render() {
return (
<NavBar activeScreen={this.state.activeScreen} setActiveScreen={this.setActiveScreen} />
)
}
This is the relevant part of the Navbar component:
class NavBar extends Component {
render() {
return (
<li className="nav-item" key={key}>
<Link className={`nav-link ${this.props.activeScreen == i.key ? 'active' : ''}`} to={i.to} onClick={() => {this.props.setActiveScreen(i.key)}}>
{i.label}
</Link>
</li>
)
}
You have to bind the setActiveScreen function to this in the constructor of your App component.
Like this:
constructor(props) {
super(props);
this.state = {
activeScreen: 'dashboard'
}
this.setActiveScreen = this.setActiveScreen.bind(this);
}
Or, as #Nico said in the comments, use an arrow function.
Like this:
setActiveScreen = key => {
// logic
}
If you have this function in a Class based component then you can update your current function to an arrow function like below.
setActiveScreen = (key) => {
this.setState({
activeScreen: key
})
}
The thing is you are accessing this without a context being given.
Another way can be you bind this to the function. Below is the example to do it the other way inside your constructor.
constructor(props) {
super(props);
this.setActiveScreen = this.setActiveScreen.bind(this);
// Other code ....
}
First every methods how use setState has to be bind in the constructor like this :
this.setActiveScreen = this.setActiveScreen.bind(this);
Secondly, just for the good practice define your props in NavBar like this :
static propTypes = {
setActiveScreen: PropTypes.func.isRequired,
}
Thirdly, is not a good practice to use arrow function in your render. So you have to possibility :
Using partial from lodash
so it looks like this :
<Link onClick={ _.partial(this.props.setActiveScreen, i.key)}}>
Or you can define a specific method in your NavBar component called by onClick and you retrieve your key in the event object provide by default by onClick

reactjs -- adding more to state when compositing a component

if I have a class
class Role extends Component {
constructor(props) {
super(props);
this.state = {
role: 'something',
value: 1
}
}
render() {
let roleStatus = [];
for (let key in this.state) {
roleStatus.push(<p key={key}>{key}: {this.state[key]}</p>)
}
return (
<div>
<div>
{roleStatus}
</div>
</div>
);
}
and then another class that uses composition (asrecommended by React doc)
class specialRole extends Component {
constructor(props) {
super(props);
// I want to add some other attributes to this.state
}
render() {
return <role />
}
}
}
I want to be able to add another attribute, let's say name, to this.state, but when I use setState to add it, the render doesn't reflect to it. I'm wondering if it's because setState is not synchronized function. If that's the case, how should I achieve what I want to do?
What you'll want to do is think of it like a parent/child composition.
The parent has the logic and passes it to the child, which then displays it.
In your case, the parent should be Role, and the child component be something that renders Role's states, for example: RoleStatus. In addition, you can have another component called SpecialRoleStatus. Note the capitalized component names, component names should be capitalized.
The composition would look something like this:
class Role extends React.Component {
constructor(props) {
super(props)
//lots of state, including special ones
}
render() {
return(
<div>
<RoleStatus normalState={this.state.normalState} />
<SpecialRoleStatus specialState={this.state.specialState} />
</div>
)
}
}
Also, setState() won't behave like you want it to because it does not set the state of any other component other than its own component.

How to modify a prop and not reflect the change in parent

I want to change some inner property of a prop. Props claim to be immutable, but when I change the value, the parent's state value is getting changed.I read that props are immutable. But changing the value is reflecting in parent.
class ParentComp extends React.Component {
constructor(props) {
super(props);
let property = {someProperty:'ABCD'};
this.state={
myState: property
}
}
render() {
return(
<div>
Parent:{JSON.stringify(this.state.myState)}
<ChildComp pState={this.state.myState} />
</div>
);
}
}
class ChildComp extends React.Component {
render() {
this.props.pState.someProperty = '1234';
return(
<div>
Child:{JSON.stringify(this.props.pState)}
</div>
);
}
}
At the end, I see both the values are changed to 1234. In online fiddles, it is working as expected(ie., parent value is not changed.). But in my project, the parent is being changed.
How do I achieve this usecase, wherein I want to change some properties in props, and not reflect in the parent's state?
I read that props are immutable
That's not true - you only should treat them as they were immutable. In other words, they are just regular javascript Objects and you should not mutate them.
If you want to change the value of the props, you should dump the props in the state first then do the mutation on that state.
Mutating props is not best practice. So your ChildComp should be like:
class ChildComp extends React.Component {
constructor(props) {
super(props);
this.state = {
pState: props.pState,
}
}
componentDidMount() {
const { pState } = this.state.
pState.someProperty = '1234';
this.setState({
pState,
});
}
render() {
return(
<div>
Child:{JSON.stringify(this.state.pState)}
</div>
);
}
}

React.js - setState after calculation based on props

I have a component that receives images as props, performs some calculation on them, and as a result I need to update its class. But if I use setState after the calculation, I get the warning that I shouldn't update state yet... How should I restructure this?
class MyImageGallery extends React.Component {
//[Other React Code]
getImages() {
//Some calculation based on this.props.images, which is coming from the parent component
//NEED TO UPDATE STATE HERE?
}
//componentWillUpdate()? componentDidUpdate()? componentWillMount()? componentDidMount()? {
//I CAN ADD CLASS HERE USING REF, BUT THEN THE COMPONENT'S
// FIRST RENDERED WITHOUT THE CLASS AND IT'S ONLY ADDED LATER
}
render() {
return (
<div ref="galleryWrapper" className={GET FROM STATE????}
<ImageGallery
items={this.getImages()}
/>
</div>
);
} }
You should put your logic into componentWillReceiveProps (https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops) so as to do a prop transition before render occurs.
In the end what we did was run the logic in the constructor and then put the class into the initial state:
class MyImageGallery extends React.Component {
constructor(props) {
super(props);
this.getImages = this.getImages.bind(this);
this.images = this.getImages();
this.state = {smallImgsWidthClass: this.smallImgsWidthClass};
}
getImages() {
//Some calculation based on this.props.images, which is coming from the parent component
this.smallImgsWidthClass = '[calculation result]';
return this.props.images;
}
render() {
return (
<div className={this.state.smallImgsWidthClass }
<ImageGallery
items={this.images}
/>
</div>
);
}
}

Resources