I'm aware that a duplicate question exists to this one, but that didn't prove of any help to me. Here's my index.js:
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
class App extends React.Component {
constructor(props) {
super(props);
this.formSet = this.formSet.bind(this);
this.state = { editing: false, ename: "Edit", sname: "Save" };
}
formNormal() {
return (
<div>
<h4>{this.state.ename}</h4>
<p>Hello there!</p>
<button onClick={!this.formSet}>{this.props.etext}</button>
</div>
);
}
formEdit() {
return (
<div>
<h4>{this.state.sname}</h4>
<input type="textarea" defaultValue="Hello there!" />
<button onClick={this.formNormal}>{this.props.stext}</button>
</div>
);
}
formSet() {
return (this.setState({ editing: true }));
}
render() {
if (this.state.editing) {
return (this.formEdit);
}
else {
return (this.formNormal);
}
}
}
render(<App etext="Edit" stext="Save" />, document.getElementById('root'));
and here's the error:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
in App
I am kind of new to React.
Couple of mistakes:
Switch
return (this.formEdit)
to
return (this.formEdit())
and return (this.formEdit); to return (this.formEdit());.
this.formEdit is a function, what you want to render in page is what the function returns i.e. by executing this.formEdit function by this.formEdit(). You cannot render a function, but can render what a function returns i.e. a valid JSX.
Bindings are not used correctly. So i just added the whole example which will work. Have a look.
class App extends React.Component {
constructor(props) {
super(props);
this.formSet = this.formSet.bind(this);
this.formNormal = this.formNormal.bind(this);
this.formEdit = this.formEdit.bind(this);
this.state = { editing: false, evalue: 'Hello There!', ename: this.props.eText, sname: this.props.sname };
}
formNormal() {
return (
<div>
<h4>{this.state.ename}</h4>
<p>{this.state.evalue}</p>
<button onClick={this.formSet}>{this.props.etext}</button>
</div>
);
}
handleChange = (e) => {
this.setState({
evalue: e.target.value,
});
}
formEdit() {
return (
<div>
<h4>{this.state.sname}</h4>
<input type="textarea" value={this.state.evalue} onChange={this.handleChange} />
<button onClick={this.formSet}>{this.props.stext}</button>
</div>
);
}
formSet() {
this.setState({ editing: !this.state.editing });
}
render() {
if (this.state.editing) {
return (this.formEdit());
}
else {
return (this.formNormal());
}
}
}
ReactDOM.render(
<App etext="Edit" stext="Save" />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>
<div id="root">
<!-- This element's contents will be replaced with your component. -->
</div>
Related
I have been creating a react js project which is a task list. However I have been facing an error, for some reason I have found multiple errors. First for debugging purposes I created a handle delete function which will log a string to the console to show that it has been triggered. It is only meant to be triggered when the X button on the task element is clicked, however, it triggers whenever I submit the text input to create a new task. Secondly the biggest error is that the first element is always undefined no matter what I type. How do I fix this problem.
import React, { Component } from "react";
import ReactDOM from "react-dom";
import TaskList from "./index2";
class Tests extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
object: {},
object2: [],
};
this.updateInputValue = this.updateInputValue.bind(this);
this.submit = this.submit.bind(this);
}
updateInputValue(evt) {
this.setState({
value: evt.target.value,
});
}
submit() {
if (this.state.value.trim() != "") {
this.setState({
object: { task: this.state.value.trim(), id: Date.now },
object2: [...this.state.object2, this.state.object],
value: "",
});
} else {
return;
}
}
render() {
return (
<div>
<label for="text">Text 1</label>
<br />
<input
type="text"
label="text"
onChange={(evt) => this.updateInputValue(evt)}
onSubmit={this.submit}
value={this.state.value}
/>
<br />
<button onClick={this.submit} style={{ height: 50, width: 60 }}>
Submit
</button>
<br></br>
<TaskList
allTasks={this.state.object2}
handleDeleteFunction={console.log("handle delete has been triggered")}
/>
</div>
);
}
}
export default Tests;
ReactDOM.render(<Tests />, document.getElementById("root")
);
And my taskList code
import React from "react";
import "./woah.css";
export default function TaskList({ allTasks, handleDeleteFunction }) {
return (
<ul>
{allTasks.map(({ task, id }) => (
<li key={id} id="task">
<p>{task}</p>
<button onClick={() => handleDeleteFunction}>X</button>
</li>
))}
</ul>
);
}
Try this:
submit() {
if (this.state.value.trim() != "") {
const newObj = { task: this.state.value.trim(), id: Date.now };
this.setState({
object: newObj,
object2: [...this.state.object2, newObj],
value: "",
});
} else {
return;
}
}
You are referencing the current object in state, which is empty.
I'm currently following this and I did get it to work. But I would like to know if there is a way to stop the Query Render from reloading the data when calling this.setState(). Basically what I want is when I type into the textbox, I don't want to reload the data just yet but due to rendering issues, I need to set the state. I want the data to be reloaded ONLY when a button is clicked but the data will be based on the textbox value.
What I tried is separating the textbox value state from the actual variable passed to graphql, but it seems that regardless of variable change the Query will reload.
Here is the code FYR.
const query = graphql`
query TestComponentQuery($accountId: Int) {
viewer {
userWithAccount(accountId: $accountId) {
name
}
}
}
`;
class TestComponent extends React.Component{
constructor(props){
super(props);
this.state = {
accountId:14,
textboxValue: 14
}
}
onChange (event){
this.setState({textboxValue:event.target.value})
}
render () {
return (
<div>
<input type="text" onChange={this.onChange.bind(this)}/>
<QueryRenderer
environment={environment}
query={query}
variables={{
accountId: this.state.accountId,
}}
render={({ error, props }) => {
if (error) {
return (
<center>Error</center>
);
} else if (props) {
const { userWithAccount } = props.viewer;
console.log(userWithAccount)
return (
<ul>
{
userWithAccount.map(({name}) => (<li>{name}</li>))
}
</ul>
);
}
return (
<div>Loading</div>
);
}}
/>
</div>
);
}
}
Okay so my last answer didn't work as intended, so I thought I would create an entirely new example to demonstrate what I am talking about. Simply, the goal here is to have a child component within a parent component that only re-renders when it receives NEW props. Note, I have made use of the component lifecycle method shouldComponentUpdate() to prevent the Child component from re-rendering unless there is a change to the prop. Hope this helps with your problem.
class Child extends React.Component {
shouldComponentUpdate(nextProps) {
if (nextProps.id === this.props.id) {
return false
} else {
return true
}
}
componentDidUpdate() {
console.log("Child component updated")
}
render() {
return (
<div>
{`Current child ID prop: ${this.props.id}`}
</div>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
id: 14,
text: 15
}
}
onChange = (event) => {
this.setState({ text: event.target.value })
}
onClick = () => {
this.setState({ id: this.state.text })
}
render() {
return (
<div>
<input type='text' onChange={this.onChange} />
<button onClick={this.onClick}>Change ID</button>
<Child id={this.state.id} />
</div>
)
}
}
function App() {
return (
<div className="App">
<Parent />
</div>
);
}
I have a menu button that when pressed has to add a new component. It seems to work (if I manually call the function to add the components they are shown). The problem is that if I click the button they are not shown, and I suppose because I should use setState to redraw them. I am not sure how to call the setState of another component within another function/component.
This is my index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Menu from './Menu';
import * as serviceWorker from './serviceWorker';
import Blocks from './Block.js';
ReactDOM.render(
<div className="Main-container">
<Menu />
<Blocks />
</div>
, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers:
serviceWorker.unregister();
Then I have the Menu.js
import React from 'react';
import './Menu.css';
import {blocksHandler} from './Block.js';
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleAdd = this.handleAdd.bind(this);
}
handleAdd(event) {
blocksHandler.add('lol');
console.log(blocksHandler.render());
}
render() {
return (
<div className="Menu">
<header className="Menu-header">
<button className="Menu-button" onClick={this.handleAdd}>Add block</button>
</header>
</div>
);
}
}
export default Menu;
And finally the Block.js
import React from 'react';
import './Block.css';
// this function adds components to an array and returns them
let blocksHandler = (function() {
let blocks = [];
return {
add: function(block) {
blocks.push(block);
},
render: function() {
return blocks;
}
}
})();
class Block extends React.Component {
constructor(props) {
super(props);
this.state = {
title: '',
content: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.title);
event.preventDefault();
}
render() {
return (
<div className="Block-container">
<form onSubmit={this.handleSubmit}>
<div className="Block-title">
<label>
Block title:
<input type="text" name="title" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<div className="Block-content">
<label>
Block content:
<input type="text" name="content" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<input type="submit" value="Save" />
</form>
</div>
);
}
}
class Blocks extends React.Component {
render() {
return (
<div>
{blocksHandler.render().map(i => (
<Block key={i} />
))}
</div>
)
}
}
export default Blocks;
export {blocksHandler};
I am a React complete beginner so I'm not even sure my approach is correct. Thank you for any help you can provide.
Below I've knocked up a really simple Parent / Child type setup,..
The Parent is responsible for rendering the Buttons, I just used a simple numbered array here. When you click any of the buttons, it calls the setState in the Parent, and this in turns causes the Parent to re-render it's Children.
Note: I've also used React Hooks to do this, I just find them more
natural and easier to use. You can use Classes, the same principle
applies.
const {useState} = React;
function Child(props) {
const {caption} = props;
const {lines, setLines} = props.pstate;
return <button onClick={() => {
setLines([...lines, lines.length]);
}}>
{caption}
</button>;
}
function Parent(props) {
const [lines, setLines] = useState([0]);
return lines.map(m => <Child key={m} caption={`Click ${m}`} pstate={{lines, setLines}}/>);
}
ReactDOM.render(<React.Fragment>
<Parent/>
</React.Fragment>, document.querySelector('#mount'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="mount"></div>
Instead of creating blocksHandlers as a separate function ,you can have it nside the Menu.js like as follows
*
class Block extends React.Component {
constructor(props) {
super(props);
this.state = {
title: '',
content: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.title);
event.preventDefault();
}
render() {
return (
<div className="Block-container">
<form onSubmit={this.handleSubmit}>
<div className="Block-title">
<label>
Block title:
<input type="text" name="title" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<div className="Block-content">
<label>
Block content:
<input type="text" name="content" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<input type="submit" value="Save" />
</form>
</div>
);
}
}
Menu.js
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {value: '',blocksArray:[]};
this.handleAdd = this.handleAdd.bind(this);
}
handleAdd() {
this.setState({
blocksArray:this.state.blocksArray.push(block)
})
}
renderBlocks = ()=>{
this.state.blocksArray.map(block=> <Block/>)
}
render() {
return (
<div className="Menu">
<header className="Menu-header">
<button className="Menu-button" onClick={()=>this.handleAdd()}>Add block</button>
</header>
{this.renderBlocks()}
</div>
);
}
}
export default Menu;
I am sorry for asking this, since I think this has been asked before. However I do not understand react enough, or at all to understand the answers people have given on other questions. Neither to implement them into the code I have.
this is the main code:
import React from 'react';
import ReactDOM from 'react-dom';
import TodoItem from './components/TodoItem';
class App extends React.Component {
componentWillMount() {
this.setState({todoList: [], inputField: ''});
}
handleInput(event) {
this.setState({inputField: event.target.value});
}
addTodo(event) {
if(this.state.inputField.length === 0 || event.keyCode && event.keyCode !== 13) return;
event.preventDefault();
var newTodo = {
text: this.state.inputField,
created_at: new Date(),
done: false
};
var todos = this.state.todoList;
todos.push(newTodo);
this.setState({todoList: todos, inputField: ''});
}
render() {
return (
<div>
<ul>
{
this.state.todoList.map(function(todo, index){
return (
<TodoItem todo={todo} key={index} />
);
})
}
</ul>
<div>
<label htmlFor="newTodo">Add Todo item</label>
<input name="newTodo" value={this.state.inputField} type="text" onKeyUp={this.addTodo.bind(this)} onChange={this.handleInput.bind(this)} />
<button type="button" onClick={this.addTodo.bind(this)} >Add</button>
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
and this is the other part:
import React from 'react';
class TodoItem extends React.Component {
constructor(props) {
super(props);
this.state = {todo: props.todo};
}
toggleDone(event) {
var currentTodo = this.state.todo;
if (currentTodo.done) {
currentTodo.done = false;
} else {
currentTodo.done = true;
}
this.setState({todo: currentTodo});
}
removeTodo(event) {
event.preventDefault();
var todos = this.state.todoList;
todos.remove(this);
}
render() {
return (
<li>
<input type="checkbox" onChange={this.toggleDone.bind(this)} />
<span className={this.state.todo.done ? 'done' : ''} >
{this.state.todo.text}</span>
<button type="button" onClick={this.removeTodo.bind(this)}
>X</button>
</li>
);
}
}
export default TodoItem;
Firstly I had the remove function in the main code, but I got an uncaught type error than because it couldn't find the bind??
And when I put it in the second part of code, I get a cannot read property "remove" of undefined error.
Any help would be awesome!
Thx upfront
Remove removeTodo function from TodoItem component and put it in App component. Pass this function as prop to TodoItem component and call this function at cross button click. Remember, bind removeTodo function to App component after moving it.
import React from 'react';
class App extends React.Component {
constructor() {
super();
this.state = {
data: 'First Comes First Serves'
}
this.updateState = this.updateState.bind( this );
};
updateState( e ) {
console.log( e );
// console.log( e.target );
this.setState( {data: e} );
}
render() {
return (
<div>
<Content myDataProp = { this.state.data } updateStateProp = { this.updateState }></Content>
</div>
);
}
}
class Content extends React.Component {
render() {
return (
<div>
<input type="text" value={ this.props.myDataProp } id="id" />
<div>
<button onClick={ this.props.updateStateProp( document.getElementById( 'id' ).value )}>Click</button>
</div>
<h3>{ this.props.myDataProp }</h3>
</div>
);
}
}
export default App;
Error;
Uncaught TypeError: Cannot read property 'value' of null
I'm very new at React.
Here, input value is assigned as 'First come..'(I suppose so), but error appears,
Thanks in advance.
When you are working with React, it is usually best to completely forget about operations that work directly on the DOM like getElementById. They will either not work at all or in unexpected ways.
Instead, have a look at the excellent React forms documentation.
The idiomatic way to do forms in React is to implement the onChange event handler of the input and store the current value in the compoenent's state. Then you can use it access and use it when the user presses the button.
I totally agree with Timo. I have modified the component for you:
class App extends React.Component {
constructor() {
super();
this.state = {
data: 'First Comes First Serves'
}
this.updateState = this.updateState.bind(this);
};
updateState(e) {
this.setState({ data: e.target.value });
}
render() {
return (
<div>
<Content myDataProp={this.state.data} updateStateProp={this.updateState}></Content>
</div>
);
}
}
class Content extends React.Component {
render() {
return (
<div>
<input type="text" onChange={this.props.updateStateProp} id="id" />
<div>
<button onClick={this.props.updateStateProp}>Click</button>
</div>
<h3>{this.props.myDataProp}</h3>
</div>
);
}
}
export default App;
Let me know if you are able to get it working.