React. components are not rendered in the order of state change - reactjs

In react, assume I have Input component with prop name = A, B, C.
they are rendered in the order
render() {
return(
<Input name="A" />
<Input name="B" />
<Input name="C" />
);
}
then I change the state of C and A in the order first C then A.
component A and C re rendered in the order first A then C. they are not rendered in the order of state change(C then A)
See the code snippet given below.
I found the out put as
set state of C
set state of B
set state of A
Render of A
Render of B
Render of C
class Input extends React.Component {
componentWillMount() {
this.props.addInput(this);
}
state = {
error: false
}
check() {
console.log("set state of", this.props.name)
this.setState({
error: true
})
}
render() {
console.log("Render of", this.props.name)
return (
<input />
);
}
}
class Hello extends React.Component {
constructor(props) {
super(props);
this.inputs = {};
}
addInput(component) {
this.inputs[component.props.name] = component;
console.log(this.inputs);
}
checkAll() {
const inputs = this.inputs;
Object.keys(inputs).reverse().forEach(name => {
inputs[name].check()
});
}
render() {
return (
<div>
<Input addInput={(c) => this.addInput(c)} name="A"/>
<Input addInput={(c) => this.addInput(c)} name="B"/>
<Input addInput={(c) => this.addInput(c)} name="C"/>
<button onClick={() => this.checkAll()}>click here</button>
</div>
);
}
}
ReactDOM.render(
<Hello initialName="World"/>,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>

This is how JSX is supposed to work by default.
If you want to render components in an order of last state change, you'll have to put all your components either into an array or have a collection of componentName: componentInstance, also having a collection [or an array] of componentName: lastUpdated (or array item form { componentName: string, lastUpdated: Date }) in which you can modify each component's lastUpdated value and then sort your componentName: componentInstance collection or array by the date values.
Then just .map in the JSX.

Related

Add element text

I am trying to do to do list. Onclick I want to add tag p with value from input, but it gives me error. I want to do like this:
I have array in state with name items, onclick I add value from input to my item array, then in return I make map function which return tag p with text value. What I am doing wrong?
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {text: '', items: []};
this.handleChange = this.handleChange.bind(this);
this.showValue = this.showValue.bind(this);
}
handleChange(event) {
this.setState({text: event.target.value});
}
showValue() {
var newItem = {
text: this.state.text
};
this.setState({
items: this.state.items.concat(newItem)
})
}
render() {
return (
<div>
<input type="text" value={this.state.text} onChange={this.handleChange} />
<button onClick={this.showValue}>Add</button>
{this.state.items.map(function() {
<p>{this.state.text}</p>
})}
</div>
);
}
}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
In your example, you're using your state inside the mapping function, which is not what you want to do. Instead, you want to map every item inside the state, that's why you begin that part of the code with this.state.items.map.
In your map function, you get your individual items, so you can actually use your item that you added before to your state and get the text by doing {item.text}.
You need to make sure you're actually returning the <p> too inside your map function, like:
{
this.state.items.map(function(item) {
return <p>{item.text}</p>;
})
}
Working code here:

Push an item in array using ReactJS

class Demo extends React.Component{
constructor (){
super();
this.state = {
list : ['car','map', 'house']
}
}
inputValue(e){
var x = e.target.value;
console.log(x)
}
addValue(){
this.state.list.push();
this.setState({list: this.state.list});
}
render(){
return(
<div>
<input onChange={this.inputValue} type="text"/>
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
)
}
}
ReactDOM.render(
<Demo/>,
document.getElementById('test')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="test"></div>
Using my code, how can i push the value from <input onChange={this.inputValue} type="text"/> in list : ['car','map', 'house']. I use for this addValue function, but i can't insert the x variable from inputValue function in push() from addValue function. How to do this using my code?
You need a state value for the text-input so that your addValue() function knows what to use when its time to add a new item. The text state will be updated with anything the user types.
Working demo: https://codesandbox.io/s/magical-feynman-fze1n
import React from "react";
class Demo extends React.Component {
constructor() {
super();
this.state = {
text: "",
list: ["car", "map", "house"]
};
}
inputValue(e) {
this.setState({
text: e.target.value
});
}
addValue() {
const text = this.state.text;
this.setState({ list: [...this.state.list, text] });
}
render() {
return (
<div>
<input onChange={this.inputValue.bind(this)} type="text" />
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
);
}
}
export default Demo;
Also, refrain from doing direct state-mutations like this.state.list.push(blah). This is against React principles and can lead to unwanted visual side-effects. If you need to reference an existing state, try to create a copy of it instead. In the case for you list, we use the spread-operator to create a shallow-copy and then added the new item to the array..
Since React is all about small components and reusability consider breaking it up into two separate components... That way, if you need a form anywhere else you can reuse it...
Here is your Demo:
class Demo extends Component {
state = { list: ['car', 'map', 'house'] };
addItem = item => {
this.setState({ list: [item, ...this.state.list] });
};
render() {
return (
<div>
<Form addItem={this.addItem} />
{this.state.list.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}
}
And here is the Form:
class Form extends Component {
state = { item: '' };
handleChange = event => {
this.setState({ item: event.target.value });
};
handleSubmit = event => {
event.preventDefault();
this.props.addItem(this.state.item);
this.setState({ item: '' });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
value={this.state.item}
onChange={this.handleChange}
/>
</form>
);
}
}
Live Demo: https://stackblitz.com/edit/react-611uzp

React pass multi state between two components

i found a gist about how to pass state between two components.
Here the jsbin
But how about the multi state?
I want two input fields and show the entered text in other components when i edit it.
i tried edited like this
this.state = {
fieldVal: "" //first input state
otherFieldVal: "" //second
}
and
//input onChange
onUpdate = name => (event) => {
this.setState({ [name]: event.target.value });
};
with no luck.
How can i made it work on multi state for multi input fields ?
Don't need to keep state in both Child and parent. You can write your child component like below, and you can access tow states dynamically by using data-attirb or you can folloe #Isaac 's answer.Keep the state in Child and pass state to Parent or keep the event to Parent from Child.
export class Child extends React.Component {
update = (e) => {
this.props.onUpdate(e.target)
};
render() {
return (
<div>
<h4>Child</h4>
<input
type="text"
placeholder="type here"
onChange={this.update}
data-state = "fieldVal"
value={this.props.fieldVal}
/><br/><br/>
<input
type="text"
placeholder="type here"
onChange={this.update}
data-state = "otherFieldVal"
value={this.props.otherFieldVal}
/>
</div>
)
}
}
export class OtherChild extends React.Component {
render() {
return (
<div>
<h4>OtherChild</h4>
Value in OtherChild Props passedVal1: {this.props.passedVal1} <br/>
Value in OtherChild Props passedVal2: {this.props.passedVal2}
</div>
)
}
}
and in parent :
class App extends Component {
onUpdate = (data) => {
this.setState({
[data.dataset.state]: data.value
})
};
render() {
return (
<div>
<h2>Parent</h2>
Value in Parent Component State fieldVal: {this.state.fieldVal} <br/>
Value in Parent Component State otherFieldVal: {this.state.otherFieldVal}
<br/>
<Child onUpdate={this.onUpdate} fieldVal= {this.state.fieldVal} otherFieldVal ={this.state.otherFieldVal}/>
<br />
<OtherChild passedVal1={this.state.fieldVal} passedVal2={this.state.otherFieldVal}/>
</div>
);
}
}
demo
renderInput = (prop) => {
return (
<Input
onChange={(event) => {
this.setState({ [prop]: event.target.value });
}}
/>
)
}
render() {
<div>
{this.renderInput('name')}
{this.renderInput('age')}
</div>
}
We can set a renderInput method and render different input using parameter to achieve your objective

get value from child component

I am new to React and need some help to my specific situation. I have a top-level app.js where I render
export default class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
currentGuess: '',
historicGuess: '',
result: ''
};
}
handleCurrentGuess(event) {
console.log(event)
this.setState({currentGuess: event.target.value})
}
handleSend() {
console.log(this.state.currentGuess)
}
render() {
return (
<div className="wrapper">
<Header />
<Logic handleCurrentGuess={this.handleCurrentGuess}/>
<Result />
</div>
)
}
}
The component has to be stateful, and I enter the currentGuess value into state.
The <Logic /> looks like this:
export default function Logic(props) {
console.log(props)
return (
<div className="logic">
<form>
<input type="text" onChange={props.handleCurrentGuess}/>
<button onClick={(e) => {
e.preventDefault()
props.handleSend
}}>Send</button>
</form>
</div>
)
}
The issue now is that I cannot find documentation on how to pass both pass the function on to the AND get returned a value from the input. Most docs show onChange via the input directly, but I want to fetch the value ONLY when someone clicks on the submit button (or hits enter). So,
how do I pass the correct function to the child, and how do I get the text value back on button press within the Logic component?
If you want to console.log the state right now (for testing purposes obviously) here is the two problems with your code.
First, you are not passing your handleSend function as a prop to Logic component.
Second, on your button, you are not invoking this handleSend function in your onClick handler.
Here is a working example.
const Logic = props => (
<div className="logic">
<form>
<input type="text" onChange={props.handleCurrentGuess} />
<button onClick={props.handleSend}>Send</button>
</form>
</div>
);
class Page extends React.Component {
state = {
currentGuess: '',
historicGuess: '',
result: ''
};
handleCurrentGuess = event =>
this.setState({ currentGuess: event.target.value })
handleSend = (e) => {
e.preventDefault();
console.log(this.state.currentGuess)
}
render() {
return (
<div className="wrapper">
<Logic
handleCurrentGuess={this.handleCurrentGuess}
handleSend={this.handleSend} />
</div>
)
}
}
ReactDOM.render(<Page />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I slightly changed the code. Use some arrow functions so no need to .bind them, remove the unnecessary constructor, use class-fields, etc. I also used the function reference for onClick in the button.

React - Redux form - state gets set but then resets almost instantly

I have a component in which I'm trying to populate a <Select /> component with some options from my props. When the component mounts, I set the state of jobNumbers to an empty array.
I have two dropdowns in which one's values, depend on the other's selected value. When the value is selected, I run an onChange function to populate the second dropdown. The only problem is when I do this.setState({jobNumbers: [...array elements...]}), the state still shows the jobNumbers array to be empty. The function that actually does the state setting is getCustomerOptions().
Here is my component in it's entirety (it's not TOO terribly long)
import React from 'react';
import SelectInput from '../../components/SelectInput';
import LocationSelector from '../../components/LocationSelector';
import { Field } from 'redux-form/immutable';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
class InputCurrentCustomerLocation extends React.Component {
constructor(props) {
super(props);
this.state = {
jobNumbers: [],
};
this.getCustomerOptions = this.getCustomerOptions.bind(this);
this.onChange = this.onChange.bind(this);
}
componentWillMount() {
if (this.props.active) {
this.props.input.onChange(this.props.active);
}
}
onChange(event) {
if (this.props.input.onChange) {
this.props.input.onChange(event.value); // <-- To be aligned with how redux-form publishes its CHANGE action payload. The event received is an object with 2 keys: "value" and "label"
// Fetch our Locations for this customer
this.props.handleCustomerLocationFetch(event.value);
this.getCustomerOptions(event);
}
}
getCustomerOptions(event) {
let options = [];
if(event) {
this.props.options.forEach((option, index) => {
if(option.value === event.value) {
console.log('props options', this.state);
this.setState({ jobNumbers: this.props.options[index] });
console.log('state options', this.state);
}
})
}
}
render() {
const { meta } = this.props;
return (
<div>
<Select
options={this.props.options} // <-- Receive options from the form
{...this.props}
value={this.props.input.value || ''}
// onBlur={() => this.props.input.onBlur(this.props.input.value)}
onChange={this.onChange.bind(this)}
clearable={false}
/>
{meta.error && <div className="form-error">{meta.error}</div>}
{this.props.activeLocations ? false : (
<div>
<div>
<p> Select a location </p>
<Field
name="locations"
component={props =>
<LocationSelector
{...props}
// active={this.props.activeLocations}
locations={this.props.locations}
/>
}
/>
</div>
<div>
<p> Select a job number</p>
<Field
name="jobNumber"
component={props =>
<Select
options={this.state.jobNumbers}
value={this.props.input.value || ''}
onChange={this.onChange.bind(this)}
clearable={false}
/>
}
/>
</div>
</div>
)}
</div>
);
}
}
export default InputCurrentCustomerLocation;
I'm relatively new to React and redux and I'm not entirely sure why this is happening. Shouldn't the state be populated?
this.setState({ jobNumbers: this.props.options[index] });
is async.
so when you do a console log on the state after setState, the state still won't change.
you should check the value on componentDidUpdate(prevProps, prevState) , and print it there.

Resources