React Array wont update, when adding items to it - reactjs

I have made To-Do list with react, but when I submit new entry, it wont show on the list. I have no clue where the problem is.
class TodoList extends React.Component {
constructor(){
super();
This array doesn't get updated like it should:
this.state ={
items:["item1", "item2"]
};
}
Here I add new task:
addTask = (task) => {
this.setState((prevState) => ({
items: prevState.items.concat([task])
}));
}
render(){
return(
<div>
<AddTodo addNew ={this.addTask}/>
<ShowEntries entries={this.state.items}/>
</div>
)
}
}
class AddTodo extends React.Component {
constructor(){
super();
this.handeAddNew = this.handeAddNew.bind(this);
this.state ={
task: ""
};
}
Here I update the state, it works fine:
update(e){
this.setState({
task: e.target.value
});
}
Here I have passed the addTask function as a prop. This might be where the problem is?
handeAddNew(){
this.props.addNew(this.state.task);
this.setState({
task: ""
});
}
render(){
return(
<div>
<input type="text"
onChange={this.update.bind(this)}></input>
<button onClick={this.handleAddNew}>Add</button>
</div>
)
}
}
ShowEntries class works fine too:
class ShowEntries extends React.Component{
render(){
var list =this.props.entries.map((task) =>{
return <li>{task}</li>
});
return(
<div>
<ul>
{list}
</ul>
</div>
)
}
}
export default TodoList
Any ideas?

You should bind addTask function.
constructor(){
super();
this.addTask = this.addTask.bind(this);
}
Or change addTask to arrow function.
like
addTask = (task) => {
/* add task*/
}
handleAddNew also needs binding.
this.handleAddNew = this.handleAddNew.bind(this);

If you are relying on previous state to update the next state, you must use functional setState in React, like so:
addTask = (task) => {
this.setState((prevState) => ({
items: prevState.items.concat([task])
}));
}
That, along with binding all functions used (preferrably inside the constructor itself), should solve your issue.

you misspelled handeAddNew.
make it handleAddNew also make sure you bind your methods handleAddNew = () => { //code }

Related

ReactJS - Pass Updated Value To Sub-Component Method

I'm working on an environment that is basically set up with a Main Component like this:
class MainComponent extends Component {
constructor(props) {
super(props);
this.state = {
selectedValues: []
};
}
render() {
const { selectedValues } = this.state;
return (
// Other components
<SubComponent selectedValues = {selectedValues} />
// Other components
);
}
}
export default MainComponent;
And a Sub Component like this:
class SubComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isExporting: false,
selectedValues: props.selectedValues
};
}
performTask = () => {
this.setState({ isWorking: true });
const { selectedValues } = this.state;
console.log(`Selected Values: ${selectedValues}`);
fetch('/api/work', {
method: 'GET'
})
.then(res => res.json())
.then((result) => {
// Handle the result
this.setState({ isWorking: false });
})
.catch((error) => {
console.log(error);
this.setState({ isWorking: false });
});
};
render() {
const { isWorking } = this.state;
return (
<Button
bsStyle="primary"
disabled={isWorking}
onClick={() => this.performTask()}
>
{isWorking ? 'Working...' : 'Work'}
</Button>
);
}
}
SubComponent.propTypes = {
selectedValues: PropTypes.arrayOf(PropTypes.string)
};
SubComponent.defaultProps = {
selectedValues: []
};
export default SubComponent;
In the Main Component, there are other components at work that can change the selectedValues. The functionality I'd like to see is that when the performTask method fires, it has the most recent and up to date list of selectedValues. With my current setup, selectedValues is always an empty list. No matter how many values actually get selected in the Main Component, the list never seems to change in the Sub Component.
Is there a simple way to do this?
I would suggest you 2 of the following methods to check this problem:
Maybe the state.selectedItems doesn't change at all. You only declare it in the contractor but the value remains, since you didn't setState with other value to it. Maybe it will work if you will refer to this.props.selectedItems instead.
Try to add the function component WillReceiveProps(newProps) to the sub component and check the value there.
If this method doesn't call, it means the selectedItems doesnt change.
Update if some of it works.
Good luck.
selectedValues in SubComponent state has not updated since it was set in SubComponent constructor. You may need to call setState again in componentWillReceivedProps in SubComponent

TODO project is not woking properly

Components ->
Box
Todolist
Add
AddModal
Main component App
But it is not working that is when I add a new task. It does not get added properly.
I think I cannot use this.setstate twice in a function.
Hope I am correct
Here is given the main component.
App.js :
import React, { Component } from 'react';
import './App.css';
import Box from './Components/Box';
import Add from './Components/Add';
import Todolist from './Components/Todolist';
class App extends Component {
constructor(props) {
super(props);
this.state = {
lists: '',
inputValue: '',
itemArray: []
}
}
onAddTask = () => {
this.setState ({
lists: this.state.inputValue
});
const item = this.state.itemArray;
const title = this.state.lists;
item.push({ title })
this.setState(prevState => ({
itemArray: [...prevState.lists, title]
}))
}
updateInputValue = (event) => {
this.setState({
inputValue: event.target.value
});
}
render() {
let length = this.state.itemArray.length;
return (
<div className="App">
<Box createTodo = {
<div>
{this.state.itemArray.map((itemArr) => {
return (
<div className="box">
<Todolist tasks = {itemArr} />
</div>
)
})}
</div>
}>
</Box>
<Add addTask = {this.onAddTask} inputValues = {this.updateInputValue} inputV = {this.state.inputValue} />
</div>
);
}
}
export default App;
Your addTasks function is not correct, you are mixing up things here.
In your inputValue you save the current value from the input field right? So if you write the following
this.setState({
lists: this.state.inputValue
});
you set your todo list to this single value. And your todo list is not an array anymore.
Secondly, state is imutable. So if you write the following
this.state.itemArray.push({ title });
the state will not be updated. What you actually want is the following:
onAddTask = () => {
this.setState({
itemArray: [...this.state.itemArray, this.state.inputValue]
})
}
And I'm not sure what the lists property on the state is for. You don't use it anywhere besides in your onAddTask function. So I guess you can remove it.

How to setProps in ReactJS

I have 2 Components one called NodeWidget and another called PopupWidget. In the NodeWidget it has a Model assigned to it which looks like the following:
PopupModel
export class PopupModel {
question: string;
model: string;
constructor(question: string, model: string) {
this.question = question;
this.model = model;
}
}
The parent Component is NodeWidget which passes in the Model to the PopupWidget with data in.
NodeWidget
{ this.state.showComponent ?
<PopupWidget model={this.props.popupModel} /> :
null
}
Then finally in the child Component we have this code:
export interface PopupWidgetProps {
model: PopupModel;
}
export interface PopupWidgetState { }
export class PopupWidget extends React.Component<PopupWidgetProps, PopupWidgetState> {
constructor(props: PopupWidgetProps) {
super(props);
this.state = { };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this.props);
}
render() {
return (
<div className="popup">
<div className="popup_inner">
<h1>TEST</h1>
<input type="text" value={this.props.model.question} placeholder="Write a question..." />
<button onClick={this.handleClick}>close me</button>
</div>
</div>
);
}
}
I want to be able to bind the value of the input to the model and then for it to update the original model in the Parent Component, am i doing this correctly as it does not seem to work.
You can do this to pass the input result to parent component on the button click:
PopupWidget :
export class PopupWidget extends React.Component<PopupWidgetProps, PopupWidgetState> {
constructor(props: PopupWidgetProps) {
super(props);
this.state = { question: '' };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.inputResult(this.state.question)
}
render() {
return (
<div className="popup">
<div className="popup_inner">
<h1>TEST</h1>
<input type="text" value={this.state.question} onChange={(question) => { this.setState({ question })}} placeholder="Write a question..." />
<button onClick={this.handleClick}>close me</button>
</div>
</div>
);
}
}
NodeWidget :
constructor(props) {
super(props);
this.getInputResult = this.getInputResult.bind(this);
}
getInputResult(question) {
this.props.inputResult(question);
this.setState({ showComponent: false });
}
...
{ this.state.showComponent ?
<PopupWidget inputResult={this.getInputResult} /> :
null
}
Finally in PopupModel (i assume this is a react component, i don't know if you can work with simple es6 class in react):
export class PopupModel extends React.Component {
constructor(props) {
this.state = { question: '', model: '' }; // set your initial state
this.getInputResult = this.getInputResult.bind(this);
}
getInputResult(question) {
this.setState({ question }); // here's our result from the input
}
render(){
return(<NodeWidget inputResult={this.getInputResult} />);
}
}
This can be pretty boring to handle if you have multiple components between the two which have to communicate.
You can use a HOC like Redux or MobX to handle an app state that can be passed in any component, and any component can dispatch actions to update the app state, you should go for it if you have multiple cases like this.

React - how to update state in parent component onclick in child - what am I doing wrong?

I have a parent and child component and simply take the input and on click I want to sort of update the parent. My onChange function works, but onClick I get the error message: Cannot read property 'name' of undefined - meaning that I never actually updated the parent. I cant figure out what am I doing wrong because as I understand, Im am passing the function correctly. Anyone knows? Thanks!
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {name: 'Frarthur'};
this.changeName = this.changeName.bind(this);
}
changeName(newName) {
this.setState({
name: newName
});
}
handleInput() {
console.log("helloooooo", this.state.name)
}
render() {
return (
<div>
<Child name={this.state.name} onChange={this.changeName} onClick={this.handleInput}/>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleInput2 = this.handleInput2.bind(this);
}
handleChange(e) {
const name = e.target.value;
this.props.onChange(name);
}
handleInput2() {
this.props.onClick()
}
render() {
return (
<div>
<h1>
Hey my name is {this.props.name}!
</h1>
<input onChange={this.handleChange}/>
<input type="submit" onClick={this.handleInput2}/>
</div>
)
}
}
You could bind them in your constructor, like you did w/ changeName:
constructor(props) {
super(props);
this.state = {name: 'Frarthur'};
this.changeName = this.changeName.bind(this);
this.handleInput = this.handleInput.bind(this);
}
Alternatively, you could apply the proposed ES7 functionality (still considered "experimental" but extremely likely to be supported) property initializer by using fat arrow function:
handleInput = () => {
console.log("helloooooo", this.state.name)
};
Note, this may require you to update your project's configuration to support stage-0 preset for babel.
You haven't bind the
handleInput()
method in the Parent component. Here is the corrected Parent component
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { name: 'Frarthur' };
this.changeName = this.changeName.bind(this);
this.handleInput = this.handleInput.bind(this);
}
changeName(newName) {
this.setState({
name: newName
});
}
handleInput() {
console.log("helloooooo", this.state.name)
}
render() {
return (
<div>
<Child name={this.state.name} onChange={this.changeName} onClick={this.handleInput} />
</div>
)
}};

JSX props should not use .bind()

How to fix this error when I have the binding this way: previously binding in constructor solved but this is a bit complex for me:
{this.onClick.bind(this, 'someString')}>
and
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
Option 1:
Use arrow functions (with babel-plugins)
PS:- Experimental feature
class MyComponent extends Component {
handleClick = (args) => () => {
// access args here;
// handle the click event
}
render() {
return (
<div onClick={this.handleClick(args)}>
.....
</div>
)
}
}
Option 2: Not recommended
Define arrow functions in render
class MyComponent extends Component {
render() {
const handleClick = () => {
// handle the click event
}
return (
<div onClick={handleClick}>
.....
</div>
)
}
}
Option 3:
Use binding in constructor
class MyComponent extends Component {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// handle click
}
render() {
return (
<div onClick={this.handleClick}>
.....
</div>
)
}
}
I recommend you to use binding in the class constructor. This will avoid inline repetition (and confusion) and will execute the "bind" only once (when component is initiated).
Here's an example how you can achieve cleaner JSX in your use-case:
class YourComponent extends React.Component {
constructor(props) {
super(props);
// Bind functions
this.handleClick = this.handleClick.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleClick() {
this.onClick('someString');
}
onClick(param) {
// That's your 'onClick' function
// param = 'someString'
}
handleSubmit() {
// Same here.
this.handleFormSubmit();
}
handleFormSubmit() {
// That's your 'handleFormSubmit' function
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<p>...</p>
<button onClick={this.handleClick} type="button">Cancel</button>
<button type="submit">Go!</button>
</form>
);
}
}
Even though all the previous answers can achieve the desire result, but I think the snippet below worth mentioning.
class myComponent extends PureComponent {
handleOnclickWithArgs = arg => {...};
handleOnclickWithoutArgs = () => {...};
render() {
const submitArg = () => this.handleOnclickWithArgs(arg);
const btnProps = { onClick: submitArg }; // or onClick={submitArg} will do
return (
<Fragment>
<button {...btnProps}>button with argument</button>
<button onClick={this.handleOnclickWithoutArgs}>
button without argument
</button>
</Fragment>
);
}
}

Resources