import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
items: []
}
}
addItem(e) {
var itemArray = this.state.items;
itemArray.push({
text: this._inputElement.value,
key: Date.now()
});
this.setState({
items: itemArray
});
e.preventDefault();
}
render() {
return (
<div className="main">
<div className="header">
<form onSubmit={this.addItem}>
<input ref={(a) => this._inputElement = a}
placeholder="enter your message"/>
<button type="submit">add</button>
</form>
</div>
<todoItems entries={this.state.items}></todoItems>
</div>
);
}
}
class todoItems extends Component {
todoEntries = this.props.entries;
createTasks(item) {
return <li key={item.key}>{item.text}</li>
}
listItems = this.todoEntries.map(this.createTasks);
render() {
return(
<ul className="theList">
{this.listItems}
</ul>
);
}
}
export default App;
I try to make 'todo' app in react.js,
but it is not working add message.
it occurs refresh, and error console message :
bundle.js:30031 Warning: Unknown prop entries on tag.
Remove this prop from the element.
How do I should fix it ?
React components have to start with an upper-case letter otherwise it will not be recognized as such. Should be
<TodoItems.../>
Related
I'm learning React and I need help understanding how to create functions for values that are updated asynchronously in the DOM. For instance, I have a text input within a component called header that looks like this:
export default class Header extends React.Component {
constructor(props){
super(props);
}
render(){
return (
<div className="Header">
<div><input onKeyDown={this.props.onEnter} id="filter-results" className="full" type="text" placeholder="search kks"></input></div>
<div><button className="full">SEARCH</button></div>
</div>
);
}
}
, which is used to filter search results. The onEnter function tries to use the value updated in the input:
class App extends React.Component {
constructor(props){
super(props);
this.state = {
categories: [],
searchResults: [],
};
this.filterSearch = this.filterSearch.bind(this);
}
filterSearch(){
var el = document.getElementById('filter-results').value
console.log(el)
var result = this.state.categories.filter(row => {
var rx = new RegExp(el)
return rx.test(row['id'])
});
console.log(result)
}
render(){
return (
<div className="App">
<Header onEnter={this.filterSearch}/>
</div>
);
}
}
When I type something into the input, the element's value is logged to the console. The problem is, what is logged is always one character less than what I expect to see. If I type 'a', I get '', 'ab' => 'a', etc. I can understand conceptually that when the function is triggered and the logging occurs the value hasn't yet been updated, but I don't know how to wait for the value to be updated and then work with it. Can anyone help me?
Use onChange instead.
//change handler
handler(e) {
console.log(e.target.value)
}
//input's onChange event
onChange={ this.handler.bind(this) }
1) You should not be using native javascript to get value by id. This is not react way of doing.
App.js
import React from "react";
import ReactDOM from "react-dom";
import Header from "./Header";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
categories: [],
searchResults: []
};
this.filterSearch = this.filterSearch.bind(this);
}
filterSearch(value) {
console.log(value);
var result = this.state.categories.filter(row => {
var rx = new RegExp(value);
return rx.test(row["id"]);
});
console.log(result);
}
render() {
return (
<div className="App">
<Header onEnter={this.filterSearch} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// Header.js
import React from "react";
export default class Header extends React.Component {
constructor(props) {
super(props);
}
handleChange = ({ target }) => {
this.setState({
[target.name]: target.value
});
this.props.onEnter(target.value);
};
render() {
return (
<div className="Header">
<div>
<input
onChange={this.handleChange}
name="filter-results"
className="full"
type="text"
placeholder="search kks"
/>
</div>
<div>
<button className="full">SEARCH</button>
</div>
</div>
);
}
}
I am learning React and I am trying to call a function in a child component, that accesses a property that was passed from parent component and display it.
The props receives a "todo" object that has 2 properties, one of them is text.
I have tried to display the text directly without a function, like {this.props.todo.text} but it does not appear. I also tried like the code shows, by calling a function that returns the text.
This is my App.js
import React, { Component } from "react";
import NavBar from "./components/NavBar";
import "./App.css";
import TodoList from "./components/todoList";
import TodoElement from "./components/todoElement";
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: []
};
this.addNewTodo = this.addNewTodo.bind(this);
}
addNewTodo(input) {
const newTodo = {
text: input,
done: false
};
const todos = [...this.state.todos];
todos.push(newTodo);
this.setState({ todos });
}
render() {
return (
<div className="App">
<input type="text" id="text" />
<button
onClick={() => this.addNewTodo(document.getElementById("text"))}
>
Add new
</button>
{this.state.todos.map(todo => (
<TodoElement key={todo.text} todo={todo} />
))}
</div>
);
}
}
export default App;
This is my todoElement.jsx
import React, { Component } from "react";
class TodoElement extends Component {
state = {};
writeText() {
const texto = this.props.todo.text;
return texto;
}
render() {
return (
<div className="row">
<input type="checkbox" />
<p id={this.writeText()>{this.writeText()}</p>
<button>x</button>
</div>
);
}
}
export default TodoElement;
I expect that when I write in the input box, and press add, it will display the text.
From documentation
Refs provide a way to access DOM nodes or React elements created in the render method.
I'll write it as:
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: []
};
this.textRef = React.createRef();
this.addNewTodo = this.addNewTodo.bind(this);
}
addNewTodo() {
const newTodo = {
text: this.textRef.current.value,
done: false
};
const todos = [...this.state.todos, newTodo];
this.setState({ todos });
}
render() {
return (
<div className="App">
<input type="text" id="text" ref={this.textRef} />
<button onClick={this.addNewTodo}>Add new</button>
{this.state.todos.map(todo => (
<TodoElement key={todo.text} todo={todo} />
))}
</div>
);
}
}
In your approach, what you got as an argument to the parameter input of the method addNewTodo is an Element object. It is not the value you entered into the text field. To get the value, you need to call input.value. But this is approach is not we encourage in React, rather we use Ref when need to access the html native dom.
I'm currently learning React and am following this guy Youtube Videos.
However, they are outdated and it's difficult to get along with the old codes. I already tried some Stackoverflow and Google solutions, but they didn't help.
Right now am I struggling on how I can access my variable todo which I declared inside render() and update it with a my function handleDelete outside of render()?
My goal is to delete the item i clicked on.
I already tried to set my variable inside constructor() but then it wasn't possible to give it the value of this.props.todos.
My code:
import React from 'react';
import ReactDom from 'react-dom';
export default class TodoItem extends React.Component {
handleDelete(item){
let updatedTodos = this.props.todos;
updatedTodos = updatedTodos.filter((val,index) => {
return item !== val;
})
todos = updatedTodos;
};
render() {
//Add all this.props items
let todos = this.props.todos;
todos = todos.map((item, index) => {
return (
<li>
<div className="todo-item">
<span className="item-name">{item}</span>
<span className="item-remove" onClick={this.handleDelete.bind(this, item)}> x </span>
</div>
</li>);
});
return (<React.Fragment>{todos}</React.Fragment>)
};
}
(This code is later exported to index.js where it is transpiled with Babel)
Thanks for your time taken!
Update:
Here is index.js:
import React from 'react';
import ReactDom from 'react-dom';
import TodoItem from './todoItem';
class TodoComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: ["clean up", "walk doggo", "take nap"]
};
}
render() {
return (<div>
<h1>The todo list:</h1>
<ul>
<TodoItem todos={this.state.todos}/>
</ul>
</div>);
}
}
ReactDom.render(<TodoComponent/>, document.querySelector(".todo-wrapper"));
TodoComponent
import React from 'react';
import ReactDom from 'react-dom';
import TodoItem from './todoItem';
class TodoComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: ["clean up", "walk doggo", "take nap"]
};
}
handleDelete(item){
let todos = this.state.todos;
todos= todos.filter((todo) => todo !== item);
this.setState((prevState) => ({
todos: todos
}));
};
render() {
return (<div>
<h1>The todo list:</h1>
<ul>
<TodoItem todos={this.state.todos} handleDelete={this.handleDelete}/>
</ul>
</div>);
}
}
ReactDom.render(<TodoComponent/>, document.querySelector(".todo-wrapper"));
Todo Item
import React from 'react';
import ReactDom from 'react-dom';
export default class TodoItem extends React.Component {
render() {
return (
this.props.todos.map((item) => {
return (
<li>
<div className="todo-item">
<span className="item-name">{item}</span>
<span className="item-remove" onClick={() => this.props.handleDelete(item)}> x </span>
</div>
</li>
)
}))
}
}
Your code is having the problem in TodoItem that you are trying to delete items in TodoItem where you do not have access to state. And moreover If you do some actions in component and you want to get the change reflected the your components must re render. And this is possible when your state is changed. And component related to corresponding changes will re render itself
I have not tested it so there might be some typos but you have to do it like this
import React from 'react';
import ReactDom from 'react-dom';
export default class TodoItem extends React.Component {
handleDelete(item){
this.props.updateTodos(item)
};
render() {
//Add all this.props items
let todos = this.props.todos;
todos = todos.map((item, index) => {
return (
<li>
<div className="todo-item">
<span className="item-name">{item}</span>
<span className="item-remove" onClick={this.handleDelete.bind(this, item)}> x </span>
</div>
</li>);
});
return (<React.Fragment>{todos}</React.Fragment>)
};
}
import React from 'react';
import ReactDom from 'react-dom';
import TodoItem from './todoItem';
class TodoComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: ["clean up", "walk doggo", "take nap"]
};
this.updateTodos =this.updateTodos.bind(this);
}
updateTodos(item){
this.setState({todos :this.state.todos.filter((val,index) => {
return item !== val;
})})
}
render() {
return (<div>
<h1>The todo list:</h1>
<ul>
<TodoItem todos={this.state.todos} updateTodos ={this.updateTodos}/>
</ul>
</div>);
}
}
ReactDom.render(<TodoComponent/>, document.querySelector(".todo-wrapper"));
I'm suggesting a different approach for you. There are some issues you need to think better. First of all, I suggest keeping your todos as objects and let have them id and text properties.
Second, you have a separate component but you are passing to it whole todos. Instead of that, map your todos and then pass each todo to your component. In this way your management over everything will be easier.
For your situation, you need a delete handler to pass your TodoItem, then by using this handler you will do the action.
Lastly, your TodoItem does not need to be a class component, so I've changed it.
Here is a working example. I've changed your code according to my suggestions.
class Todos extends React.Component {
state = {
todos: [
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 3, text: "baz" },
]
}
deleteTodo = ( todo ) => {
const newTodos = this.state.todos.filter( el => el.id !== todo.id );
this.setState({ todos: newTodos });
}
render() {
const { todos } = this.state;
return (
<div>
<ul>
{
todos.map( todo => <TodoItem key={todo.id} todo={todo} deleteTodo={this.deleteTodo} /> )
}
</ul>
</div>
)
}
}
const TodoItem = props => {
const handleDelete = () => props.deleteTodo(props.todo);
return (
<li onClick={handleDelete}>{props.todo.text} x</li>
)
};
ReactDOM.render(<Todos />, 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>
Also, last night I wrote a small example for another question. But it got bigger and bigger and I did not post it. But here is a small, simple Todo example. Of course there must be some improvements but you can examine it as an example.
https://codesandbox.io/s/01v3kmp9vv
Edit
If you don't want to use class-fields and install another Babel plugin, just change the related part with this:
class Todos extends React.Component {
consturctor( props ) {
super( props );
this.state = {
todos: [
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 3, text: "baz" },
],
}
}
....rest of the code
im having a real hard time learning react and I can't quite understand how to solve this problem. I have Tournaments.js which reads tournament names from an API and displays them as clickable elements with a reference to templates.js in which I would like to simply display the name of the element that was clicked.
Tournaments.js:
import React, { Component } from "react";
const API = 'http://localhost:8080/api/tournaments';
class Tournaments extends Component {
constructor() {
super();
this.state = {
data: [],
}
}
componentDidMount() {
fetch(API)
.then((Response) => Response.json())
.then((findresponse) => {
console.log(findresponse)
this.setState({
data:findresponse,
})
})
}
testfun(e) {
var target = e.target;
console.log(target)
}
render() {
return(
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="jumbotron text-center">
{
this.state.data.map((dynamicData, key) =>
<div>
<a key={dynamicData.id} href={"/#/template"} onClick={this.testfun}>{dynamicData.name}</a>
</div>
)
}
</div>
</div>
</div>
</div>
)
}
}
export default Tournaments;
template.js:
import React, { Component } from "react";
import Tournaments from "./Tournaments";
const API = 'http://localhost:8080/api/tournaments';
class template extends Component {
constructor() {
super();
this.state = {
data: [],
}
}
render() {
return(
<div>
<h1>clicked item</h1>
</div>
)
}
}
export default template;
And my API stores data looking like this:
[{"name":"abc","id":1,"organizer":"kla"},{"name":"fsdfs","id":2,"organizer":"fsdf"}]
I've tried to use onclick to get the value that was clicked but not sure how to display it in template.js.
Thanks for any help
I think you want to put user clicked item from Tournaments.js and display in Template.js
You can try to put Template inside Tournaments
Tournaments.js
import React, { Component } from "react";
import Template from './template';
class Tournaments extends Component {
constructor() {
super();
this.state = {
data: [],
clickedData: []
}
}
testfun(e) {
var target = e.target;
console.log(target);
let newClickedData = [];
newClickedData.push(target);
this.setState({
clickedData: newClickedData
});
}
render() {
return (
<div>
...
...
<Template clickedData={this.state.clickedData} />
</div>
);
}
}
export default Tournaments;
Template.js
import _ from 'lodash';
import React, { Component } from "react";
class template extends Component {
render() {
let clickedData = this.props.clickedData;
let displayClickedData = _.map(clickedData, (item) => {
return <div>item</div>;
});
return(
<div>
{displayClickedData}
</div>
)
}
}
export default template;
I would suggest picking one of the following options:
Wrap both Tournaments and Tournament in some parent component. Then if you click on any specific tournament, you can change its ID in state. If ID is null, you render list of tournaments, if ID is defined, you render your tournament component. That way you would lose the URL effect.
You can pass ID as GET parameter in URL and then read it in your Tournament component, so your code would look similar to this <a href={`/#/template?id=${dynamicData.id}`}>{dynamicData.name}</a>
You can try out React Router which would handle all that for you
I am starting to learn react and download and follow any tutorials in internet. I am trying to build friend list.
I have tree components,
friends_container:
import React from 'react';
import AddFriend from './add_friend.jsx'
import ShowList from './show_list.jsx'
class FriendsContainer extends React.Component {
constructor() {
this.state = {
friends: ['Jake Lingwall', 'Murphy Randall', 'Merrick Christensen']
}
}
addFriend(friend) {
this.setState({
friends: this.state.friends.concat([friend])
});
}
render() {
return (
<div>
<h3> Add your friend to friendslist </h3>
<AddFriend addNew={this.addFriend}/>
<ShowList names={this.state.friends}/>
</div>
)
}
}
export default FriendsContainer;
add_friend:
import React from 'react';
class AddFriend extends React.Component {
constructor() {
this.state = {newFriend: ''};
}
updateNewFriend(e) {
this.setState({
newFriend: e.target.value
})
}
handleAddNew() {
this.props.addNew(this.state.newFriend);
this.setState({
newFriend: ''
});
}
render() {
return (
<div>
<input type="text" value={this.state.newFriend} onChange={this.updateNewFriend}/>
<button onClick={this.handleAddNew}>Add Friend</button>
</div>
)
}
}
AddFriend.propTypes = { addNew: React.PropTypes.func.isRequired };
export default AddFriend;
show_list:
import React from 'react';
class ShowList extends React.Component {
render() {
var listItems = this.props.names.map((f, i) => <li key={i}>{f}</li>);
return (
<div>
<h3>Friends</h3>
<ul>
{listItems}
</ul>
</div>
)
}
}
ShowList.defaultProps = { names: [] };
export default ShowList;
and app.jsx
import React from 'react';
import FriendsContainer from './components/friends_container.jsx';
window.React = React;
React.render(<FriendsContainer />, document.body);
as you can see on the code, I am using es6 and babel as transcompiler.
My problem, I can not type any letters into the input field to add new friend into friends list. What am I doing wrong?
In the context of your updateNewFriend method, this refers to the window object and not the current component instance. You need to bind the method before passing it as the onChange event handler. See Function::bind.
You have two options:
class AddFriend extends React.Component {
constructor() {
// ...
this.updateNewFriend = this.updateNewFriend.bind(this);
this.handleAddNew = this.handleAddNew.bind(this);
}
}
or
class AddFriend extends React.Component {
render() {
return (
<div>
<input type="text" value={this.state.newFriend} onChange={this.updateNewFriend.bind(this)}/>
<button onClick={this.handleAddNew.bind(this)}>Add Friend</button>
</div>
)
}
}
Keep in mind that Function::bind returns a new function, so binding in render creates a function every time your component is rendered, though the performance impact is negligible.