I am trying do do a simple implementation on a stateful component who's state is managed by a stateless Child. Currently the handler only triggers a console.log.
Expected behavior:
When an field is updated the parent component should trigger a console.log.
Actual behavior
The setInterest is never triggered and instead I'm getting an error about synthetic events:
This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property `nativeEvent` on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist().
The components render visually as expected, and I get no other errors either in the browser of Webpack.
Any help would be greatly appreciated.
Stateful component:
// Setting the parameters of the market
export class Parameters extends React.Component {
// Constructor setting default state
constructor ( props ) {
super ( props )
// Set the state objects
this.state = {
interest: {
pessimistic: this.props.pessimistic || 1,
historical: this.props.historical || 4,
optimistic: this.props.optimistic || 7
}
}
// Bind the functions for use in the render
this.setInterest = this.setState.bind( this )
}
// Set the parameters
setInterest( e ) {
console.log('I AM NEVER TRIGGERED')
}
// Rendering of message
render( ) {
return(
<div>
<ParametersView
handleChange={ this.setInterest }
interest={ this.state.interest } />
<DesiresView />
</div>
)
}
}
Stateless component
// Import react
import React from 'react'
export const ParametersView = ( { handleChange, interest } ) => {
return (
<div>
<span id="assumptions">
<input
onChange={ handleChange }
value={interest.pessimistic}
id="pessimistic" type="number" name="pessimistic" />
<input
onChange={ handleChange }
value={interest.historical}
id="historical" type="number" name="historical" />
<input
onChange={ handleChange }
value={interest.optimistic}
id="optimistic" type="number" name="optimistic" />
</span>
</div>
)
}
export const DesiresView = ( ) => {
return ( <p>No desire view yet</p> )
}
You have a typo.
this.setInterest = this.setState.bind( this ) needs to be
this.setInterest = this.setInterest.bind( this )
Related
I'm currently learning React and i am working through 'The Road to React' by Robin Wieruch.
I've just refactored some code from a stateful to a functional stateless component like so:
function Search(props) {
const { value, onChange, children } = props;
return (
<form>
{children} <input
type="text"
value={value}
onChange={onChange}
/>
</form>
);
}
Gets Refactored to:
const Search = ({ value, onChange, children }) => {
<form>
{children} <input
type="text"
value={value}
onChange={onChange}
/>
</form>
}
However nothing is rendering anymore. Are functional stateless components called the same was as stateful ones?
This is how I'm calling the Search component in the App class:
render() {
const { searchTerm, list } = this.state;
return (
<div className="App">
<Search
value = { searchTerm }
onChange = { this.onSearchChange }
>
Search
</Search>
<Table
list = { list }
pattern = { searchTerm }
onDismiss = { this.onDismiss }
/>
</div>
)
I'm not receiving an error at all, so i'm not getting much that's pointing me in the right direction, i'm hoping i'm just missing something silly.
Thanks in advance!
In both cases, it's a stateless function only as there's no state and it's not an class component either.
1st case is working correctly because it's returning the element with the return keyword.
2nd refactored case is also correct but you are not returning anything you need to return the element for it to be rendered.
return example
const func = () => {
... // any more calculations or code
return ( // you are returning the element here
<div>
...
</div>
)
}
If there's no calculation or any additional code and you have to return only an element you can directly return it by using (...) instead of {...} as follows
const func = () => ( // you are directly returning element
<div>
...
</div>
)
PS: for more info you can check into arrow functions
I have an input with an onChange attribute and an onKeyDown attribute. The onChange attribute receives a value of {(e) => props.change(e)} and in the parent component I assign the change prop to a handleChange function. I want to do the same thing with my onKeyDown attribute, however I face two issues:
When I use {(e) => props.key(e)}, Chrome Devtools says that key is not a prop. When I try passing {key} instead of (props) to the child component, Chrome Devtools says that key is not a function.
I'm trying to pass the prop so that I can consume the key code value in the parent component and execute some logic.
I've also tested to make sure the function is working within Home.js. If someone could give a solution and an explanation as to why this happens I would be appreciative.
Here is an example of my code:
/ App.js
export default function App() {
const key = (e) => {
if (e.keyCode === 8) {
...
}
}
const change = (e) => {
return ...
}
return
<Home
key={key}
change={change}
/>
}
/ Home.js
export default function Home(props) {
return
<input
onChange={(e) => props.change(e)}
onKeyDown={(e) => props.key(e)}
/>
}
key is a special prop in React and gets special treatment:
[…] there are two special props (ref and key) which are used by React, and are thus not forwarded to the component.
So it should work if you change the name of the prop:
App.js
export default function App() {
// …
return
<Home
myKey={key}
change={change}
/>
}
Home.js
export default function Home(props) {
return
<input
onChange={(e) => props.change(e)}
onKeyDown={(e) => props.myKey(e)}
/>
}
I'm trying to implement in my react app, two react double listbox in my component. At the moment the listboxes are filled automatically after a get request when component mounts. I need some help on how to get the selected options in each double listbox and send them to the server as json data.
I need two arrays from these lists.
This is my dual listbox classes:
import React from 'react';
import DualListBox from 'react-dual-listbox';
import 'react-dual-listbox/lib/react-dual-listbox.css';
import 'font-awesome/css/font-awesome.min.css';
export class FirstList extends React.Component {
state = {
selected: [],
};
onChange = (selected) => {
this.setState({ selected });
};
render() {
const { selected } = this.state;
return (
<DualListBox
canFilter
filterPlaceholder={this.props.placeholder || 'Search From List 1...'}
options={this.props.options}
selected={selected}
onChange={this.onChange}
/>
);
}
}
export class SecondList extends React.Component {
state = {
selected: [],
};
onChange = (selected) => {
this.setState({ selected });
};
render() {
const { selected } = this.state;
return (
<DualListBox
canFilter
filterPlaceholder={this.props.placeholder || 'Search From List 2...'}
options={this.props.options}
selected={selected}
onChange={this.onChange}
/>
);
}
}
In my component I started importing this:
import React, { useState, useEffect } from 'react'
import LoadingSpinner from '../shared/ui-elements/LoadingSpinner';
import ErrorModal from '../shared/ui-elements/ErrorModal';
import { FirstList, SecondList } from '../shared/formElements/DualListBox';
import { useHttpClient } from '../shared/hooks/http-hook';
const MyComponent = () => {
const { isLoading, error, sendRequest, clearError } = useHttpClient();
const [loadedRecords, setLoadedRecords] = useState();
useEffect(() => {
const fetchRecords = async () => {
try {
const responseData = await sendRequest(
process.env.REACT_APP_BACKEND_URL + '/components/get'
);
setLoadedRecords(responseData)
} catch (err) { }
};
fetchRecords();
}, [sendRequest]);
...
...
return (
<React.Fragment>
<ErrorModal error={error} onClear={clearError} />
<form>
<div className="container">
<div className="row">
<div className="col-md-6">
<fieldset name="SerialField" className="border p-4">
<legend className="scheduler-border"></legend>
<div className="container">
<p>SERIALS</p>
{loadedRecords ? (
<FirstList id='Serials' options={loadedRecords.firstRecordsList} />
) : (
<div>
<label>List is loading, please wait...</label>
{isLoading && <LoadingSpinner />}
</div>
)}
</div>
</fieldset>
</div>
<div className="col-md-6">
<fieldset name="SystemsField" className="border p-4">
<legend className="scheduler-border"></legend>
<div className="container">
<p>SYSTEMS</p>
{loadedRecords ? (
<SecondList options={loadedRecords.secondRecordsList} />
) : (
<div>
<label>List is loading, please wait...</label>
{isLoading && <LoadingSpinner />}
</div>
)}
</div>
</fieldset>
</div>
...
...
If anyone could guide me it'll be much appreciated.
Thanks in advance!
FirstList and SecondList are using internal state to show the selected values. Since a parent component should do the server request, it needs access to this data. This can be achieved by a variety of options:
Let the parent component (MyComponent) handle the state completely. FirstList and SecondList would need two props: One for the currently selected values and another for the onChange event. MyComponent needs to manage that state. For example:
const MyComponent = () => {
const [firstListSelected, setFirstListSelected] = useState();
const [secondListSelected, setSecondListSelected] = useState();
...
return (
...
<FirstList options={...} selected={firstListSelected} onChange={setFirstListSelected} />
...
<SecondList options={...} selected={secondListSelected} onChange={setSecondListSelected} />
...
)
Provide only the onChange event and keep track of it. This would be very similar to the first approach, but the lists would keep managing their state internally and only notify the parent when a change happens through onChange. I usually don't use that approach since it feels like I'm managing the state of something twice and I also need to know the initial state of the two *List components to make sure I am always synchronized properly.
Use a ref, call an imperative handle when needed from the parent. I wouldn't recommend this as it's usually not done like this and it's getting harder to share the state somewhere else than inside of the then heavily coupled components.
Use an external, shared state like Redux or Unstated. With global state, the current state can be reused anywhere in the Application and it might even exist when the user clicks away / unmounts MyComponent. Additional server requests wouldn't be necessary if the user navigated away and came back to the component. Anyways, using an external global state needs additional setup and usually feels "too much" and like a very high-end solution that is probably not necessary in this specific case.
By using option 1 or 2 there is a notification for the parent component when something changed. On every change a server request could be sent (might even be debounced). Or there could be a Submit button which has a callback that sends the saved state to the server.
I have a React file which displays a list of city data as a component. there is an input textbox above it which needs to accept user input. i am using state to display an initial string in the textbox, but i cannot get onChange to successfully use a function to setState. troubleshooting it with console.log i can see that when i attempt to change the state the function i am pointing to with onChange does work and changes one letter, but then the state snaps back to its default value. the problem seems to be with setState not saving the change and reverting back to the initial state after any changes are made. the text box content appears to not change at all, thought console.log shows a one letter change but then reverts back to the original state.
how do i update state? i want the user to be able to punch a number in and then compare it with the list.
import React, {Component} from 'react';
import Table from './Table';
import cities from './Cities';
class App extends Component {
state = {
userInput: "Your City Population"
}
popChanger = (event) => {
this.setState( {userInput: event.target.value} );
//console.log(event.target.value);
}
yourCity = (
<div>
<input
type='text'
onChange={this.popChanger}
value={this.state.userInput}
/>
</div>
)
render() {
return (
<div className = "App">
{this.yourCity}
<Table characterData = {cities} />
</div>
);
}
}
export default App;
setState() is saving your changes, just not in the right place,
popChanger() is an arrow function and updates the state of the App component,
yourCity has it's own this so it doesn't know about the App state.
you can either cahnge yourCity to an arrow function that returns the html you want like
class TodoApp extends React.Component {
state = {
a: ''
};
YourCity = () => (
<div>
<input type="text" onChange={this.handleChange} value={this.state.a} />
</div>
}
handleChange = e => this.setState({a : e.target.value})
render() {
return (
<div>
<this.YourCity />
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
Or, create yourCity component outside and pass the handleChange as a prop :
const YourCity = props => (
<div>
<input type="text" onChange={props.handleChange} value={props.value} />
</div>
)
class TodoApp extends React.Component {
state = {
a: ''
};
handleChange = e => this.setState({a : e.target.value})
render() {
return (
<div>
<YourCity handleChange={this.handleChange} value={this.state.a}/>
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
The state is updating but you can't see that because this.yourCity doesn't re-render
popChanger = (event) => {
this.setState( {userInput: event.target.value} );
console.log(event.target.value);
}
yourCity(){
return <div>
<input
type='text'
onChange={this.popChanger}
value={this.state.userInput}
/>
</div>
}
render() {
return (
<div className = "App">
{this.yourCity()}
</div>
);
}
}
This is my simple functional component in Vuejs
import { Label, Input, Checkmark } from "./styles";
export default {
functional: true,
model: {
prop: "checked",
event: "change"
},
props: {
checked: Boolean
},
// eslint-disable-next-line
render(h, { props, listeners}) {
console.log(listeners);
const changeHandler = listeners.change ? listeners.change : () => {};
return (
<Label>
<Input
type="checkbox"
checked={props.checked}
onChange={e => {
console.log("checked", e.target.checked);
changeHandler(e);
}}
/>
<Checkmark class="checkmark" />
</Label>
);
}
};
The component simply won't console.log when used like
<VCheckbox
checked={this.checkSelections[idx]}
onChange={e => {
this.$set(this.checkSelections, idx, e.target.checked);
}}
/>
I'm using Vuejs - 2.5.x
If I console.log my listeners they definitely log change.
Second, if I use nativeOnChange instead, it does fire.
Last and importantly, In the very same fashion, I also have a button component (functional ) and it works fine with onClick ( no use of native there ). Can anyone please let me know what's going on ?
Update -
Similar is the case with nativeOnMouseover.