React use this.state or this.props? - reactjs

I'm new to react an got a question about components.
I made a component like this:
class HeaderLabel extends React.Component {
constructor() {
super();
}
componentWillMount() {
this.setState({ value: String(this.props.value) });
}
render() {
return (
<div>
{this.props.value && this.props.value != "" ? <label className="control-label">{this.props.value}</label> : <label className="text-muted">({Translation.getPhrase("EMPTY")})</label>}
</div>
);
}
}
HeaderLabel.propTypes = {
value: PropTypes.string
}
export default HeaderLabel;
As you can see I'm using this.props.value to display the value. Is this the right way or is it better to use this.state.value?

Short answer: Use this.props.value.
Since you are taking the value directly from the props without mutating it somehow, there is no benefit at all in first storing it in the state and then using this.state.value.
Basically this boils down to the fundamental idea of React, which is that you should only ever have "one source of truth". By having value both in your props and in the state you have two sources of truth. As a developer, you should try to have a "master state" which passes the data down to its components.
Sometimes, however rarely, it can be a good idea to store the prop value directly to your state. For example, you might want to have a parent component which holds a string which is displayed in your app. This string can be modified in a modal which is opened when clicking the appropriate button. The "temporary under-edit" string would be in the modal-state, whereas the previously saved one still in the master component.
You might find this official blog article helpful: You Probably Don't Need Derived State

In this example, you are using the props value to display the data which you have received, then using this.props.value makes sense. Props are read-only, means the child is not the owner of that particular data and hence it cannot change anything on that data.
when to use this.state.value, let say a scenario where you get data from parent and you have to do some manipulations on that data before displaying, then, in that case, take props into a state and do the manipulations.

you can use both this.props.value and this.state.value. But first you must answer the following question. Depending on the answer you choose appropriate way of setting the value.
Which component handles the subsequent change of value received from this.props.value:
Change of value is coming from Parent component.Then you should use this.props.value.There is no need for you to assign this.props.value to this.state.value. Because the state is maintained inside parent component.
2.Change is handled by HeaderLabel component itself. Then you should set this.state.value.

Related

Mobx async example does not render result after action

You can look at the issue via codesandbox
Codesandbox: https://codesandbox.io/s/great-frog-m2s7h
Context
I am trying to fetch some data and populate some variable and render the value in React, essentially following the documentation await/async + runInAction example: https://mobx.js.org/actions.html#asynchronous-actions
However, when the data is fetched, React actually does not re-render. However, if you edit the text in there(i.e. change hi to his or whatever, then you see the correct value appear.
Problem
What exactly am I doing wrong with the data fetching? Why isn't the observable value not being re-rendered correctly when its been assigned a value after some data has been fetched?
This is actually one of the limitations of Mobx.
From the docs:
make(Auto)Observable only supports properties that are already defined. Make sure your compiler configuration is correct, or as workaround, that a value is assigned to all properties before using make(Auto)Observable. Without correct configuration, fields that are declared but not initialized (like in class X { y; }) will not be picked up correctly.
Just initialize the title this.title=undefined and it will work.
Its a simple mistake. MobX can not compare data of title to something that dosent exist. Stuff should have default value of some sort, even a null. hence in a constructor you need to define title default value like
constructor() {
this.title = [],
makeAutoObservable(this);
}
or, if you wish, even null
constructor() {
this.title = null,
makeAutoObservable(this);
}
Basiclaly, whenever you create some observable variable in a store - you need to define its default value in a constructor above makeAutoObservable.
Here, a forked project of yours with juswt this 1 line change to make it work https://codesandbox.io/s/suspicious-noether-fnhjw

Use state, static defaultProps or a property to hold static data

Currently, I have in my state a property holding the following value
state = {
listOfCategories = ['Bank', 'Home', 'News']
}
That piece of state is never going to change (I will use it in that component and a direct children). So I'm thinking if I should remove it from the state, which to my understanding is meant for mutable data, and put it as a property of the Class or defaultProps of that Class.
Which approach should I follow?
That piece of state is never going to change
You can even extract listOfCategories = ['Bank', 'Home', 'News'] completely outside of React tree (or put in a different file, to use it elsewhere as well).
the state, which to my understanding is meant for mutable data,
The state should be immutable because React does "shallow check" (checks if reference has been changed, not the deep comparison, which checks for changed value for nested properties as well).
a property of the Class or defaultProps of that Class.
If listCategories belongs to the current component, then it'd make sense to declare it as a property because listCategories is the component's own property, not changed by parent's props (as you said it never changes).
Well i think every solution you expose are good.
But let's come back to basic :)
You create a class with react propertys but it still a simple class.
So why not just declare this array as member of this class like this :
class test extends React.Component {
constructor(props) {
super(props);
this.state = {
test: props.FakeData
}
this.listOfCategories = ['Bank', 'Home', 'News']
}
}
simple, easy and in my opinion the best way to do it.

React Native: This synthetic event is reused for performance reasons. Trying to pass state

I got two screens and want to pass the state from one to the other.
Simplified Screen 1:
import React, { Component } from 'react';
import { View, TextInput} from 'react-native';
import { buttons } from '../../components/buttons/Buttons';
export default class LoginScreen extends Component {
constructor(props) {
super(props);
this.state = {
username: '',
}
}
render() {
return(
<View>
...
<TextInput onChange={(input) => this.setState({username: input})}></TextInput>
...
<Button.main onPress={() => this.props.navigation.navigate('Register', {username: this.state.username})} title='Create Account'/>
</View>
)
}
}
Now, if I press the button, I want to pass this.state.username to the new screen. If I don't use onChange={(input) => this.setState({username: input})} and set a value for the username manually like this:
this.state = {
username: 'Test',
}
The value gets passed without problem and I can access it from the new screen. But when I try to change this.state.username while the TextInput changes and then pass this.state.username I get the following warning:
Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property isTrusted on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist().
I don't know how to use event.persist() as proposed in the warning and the username doesn't get passed.
Sorry if this is a stupid question, but I couldn't find a solution and I'm a beginner. I would be glad if someone could help me with this :)
Try using onChangeText instead of onChange.
Adding on to accepted answer to provide a more elaborate explanation.
The behaviour observed is a combination React's intended design for events and how Javascript uses the pass-by-reference concept for passing objects into function:
React's design for events
The context here is that the function is passed to the onChange handler rather than onChangeText. onChange will pass an event object to the function while onChangeText passes a String. There is a catch to how event objects works in React.
React uses SyntheticEvent for events passed into event handlers and SyntheticEvents are pooled. So rather than creating new SyntheticEvent objects, these pooled objects are reused and all properties will be nullified after a callback is invoked (see React docs).
Pass-by-reference for objects in Javascript
In Javascript, when an object gets passed into a function as argument, the function receives the reference to that object as parameter rather than creating a copy of the object. Thus, modifying the object in the function will lead to the original object being modified.
Putting them together
Setting the passed event object parameter to state is basically storing the reference to one of the event objects in the pool into state. Since this object is reused (modified) and its content does not persist for long, the content retrieved from the reference stored in the state will quickly become irrelevant. This will cause the app to behave in unexpected ways.
The development environment detects this and thus give a warning.
The use of onChangeText - like the accepted answer recommended - will not only solve the problem but also seemingly be closer the original intention of the code.

React: setting state and default input from prop

I'm creating a app that allows users to create assessments (that other users can complete). Right now I am working on an 'edit' page, where a users loads a form that is prefilled with the relevant data - but he is then able to change these values.
However, I am having trouble with two things (that I suspect are related).
First: the input fields wont display a default value that is derived from the component state.
Second: If I set the input fields directly from the props I am no longer able to change the values.
The components gets passed a prop that is called block_data which is a dict containing key/value pairs of strings.
I'm attempting to convert load it into the state like so
constructor(props) {
super(props);
this.state = {
block: this.props.block_data,
question_list: [],
question_options: ['BO', 'SC', 'SR', 'NO'],
block_list: [],
};
(..)
}
However, this does not work. When I check in the chrome react extension the state is not set.
The input fields are all very simular to the example I've included below. Here I've set its value from the props. In this case it does display the correct initial data. But I am unable to edit it.
<input
onChange={e => this.changeIsANaturalPartOfLife(e)}
value={this.props.block_data.title}
name="title"
/>
Below is the 'on change' function. When I check the chrome react tool, I can see that only the first letter of the state is updated when I start typing. The input field does not show any changes.
changeIsANaturalPartOfLife(e, optional) {
const target = e.target;
const name = target.name;
const value = target.value;
console.log(value);
this.setState({ block: {[name]: value }});
}
I am really at a loss here on what to do. What I am trying to do here seems simple enough, yet I'm unable to advance beyond this flawed stage. Does anyone have an idea what I am doing wrong?
As you mentioned in comment: "the data is loaded from a DjangoRestAPI".
Solution of first problem:
You need to use componentwillreceiveprops lifecycle method to update the state with new props values (after successfully fetched from server) otherwise state of child component will always have the initial data of parent component.
As per DOC:
componentWillReceiveProps() is invoked before a mounted component
receives new props. If you need to update the state in response to
prop changes (for example, to reset it), you may compare this.props
and nextProps and perform state transitions using this.setState() in
this method.
Use this method:
componentwillreceiveprops(nextProps) {
// compare nextProps.block_data and this.state.block if not equal
this.setState({
block: nextProps.block_data
})
}
Solution of second problem:
When you are directly using the props instead of storing into state, then you need to update the value in parent component during onChange, because you are using this.props.value and updating this.state.value, and hence props.value will always be same and it will not allow you to type anything.

onChange event for textarea instead of get value when needed

I want to understand the performance implications of React's advocated way of handling textarea's value change.
Before React's one-direction data flow philosophy, one would do:
button.onClick( processInput(textarea.value) );
Now, one has to do
textarea.onChange( dispatch({ type: "inputChange", value: textarea.value }) );
button.onClick( dispatch({ type: "buttonClick" }) );
store(
if (action.type === "inputChange") {
this.lastInput = action.value
} else if (action.type === "buttonClick") {
processInput(this.lastInput)
}
)
Is my understanding correct? Isn't this much more events compared to before? Why spam with many useless inputChange events? If my understanding is not right, what is the correct React way of doing this?
First, you're conflating a couple different things. React's "controlled input" pattern doesn't require Redux. You can implement controlled inputs inside a component, using local component state. I have a gist discussing this concept at react-controlled-inputs.md.
Second, even if you're using Redux, you don't have to directly control the input with Redux state and actions. Whether you do so is up to you. In fact, there are times when you might want to buffer text onChange events locally in the component, and only dispatch a Redux action once the user is done typing. I've got another gist demonstrating a buffering wrapper component at FormContentsContainer.jsx.
Finally, even if you do decide to directly control an input with Redux state, I'm not exactly sure why you're saying it's "more events". The input's onChange is going to fire every time you type, regardless. It's up to you whether you choose to do something with those events like translating them into Redux actions, or ignore them. Or, if you prefer to use "uncontrolled inputs" and just ask the inputs for their values when someone clicks Submit, that's entirely up to you as well.
I think it would be better to extract actual code from the Gist and put it here.
If you need text value only on button click, you can ask it through ref
const MyComponent extends Component {
onClick() {
const input = this.refs.myInput;
const value = input.value;
// do something with the value
}
render() {
return <input type="text" ref="myInput" />
}
}
You can get access to DOM element inside of your component using refs. If you need some simple solution, that will work for you.

Resources