I am new to react and trying to create a simple todo list to understand React states and props but cant seem to understand why its not rendering the array on the screen. When the button is pressed it console logs the array of the inputs so I know that works.
here is each component currently there are no errors just nothing shows up.
App.js:
import React from "react";
import ControlPanel from "./ControlPanel";
import TodoList from "./TodoList";
class App extends React.Component {
state = { TodoList: [] };
addTask = (todoItem) => {
this.setState({ TodoList: [...this.state.TodoList, todoItem] });
console.log(this.state.TodoList);
};
render() {
return (
<div>
<ControlPanel addTask={this.addTask} />
<TodoList todoitem={this.state.TodoList} />
</div>
);
}
}
export default App;
ControlPanel.js:
import React from "react";
class ControlPanel extends React.Component {
state = { todoItem: "" };
addItem = (event) => {
event.preventDefault();
this.props.addTask(this.state.todoItem);
};
render() {
return (
<div className="ui card">
<div className="ui input">
<input
onChange={(e) => {
this.setState({ todoItem: e.target.value });
}}
value={this.state.todoItem}
type="text"
placeholder="Todo List Item"
/>
</div>
<div>
<button onClick={this.addItem} className="ui button">
Add Item
</button>
</div>
</div>
);
}
}
export default ControlPanel;
TodoList.js:
import React from "react";
import TodoItem from "./TodoItem";
const TodoList = (props) => {
const todoItems = props.TodoList?.map((todo) => {
return <TodoItem TodoItem={TodoItem} />;
});
return <div>{todoItems}</div>;
};
export default TodoList;
TodoItem.js
import React from "react";
const TodoItem = (props) => {
return <div>{this.props.TodoItem}</div>;
};
export default TodoItem;
import React from "react";
import TodoItem from "./TodoItem";
const TodoList = (props) => {
const todoItems = props.TodoList?.map((todo,idx) => {
return <TodoItem TodoItem={todo} key={idx} />; // idx or any unique key
});
return <div>{todoItems}</div>;
};
export default TodoList;
More information for key
https://reactjs.org/docs/lists-and-keys.html
Related
I am trying to fetch data from GitHub user API and store the JSON in an array, and then and then loop through the array and create Card components dynamically on submission of form, but the Card components doesn't render even though the array is updated with the newly added user.
I have added relevant code files and image of React component tree on dev console before and after valid form submission. Thanks.
App.js
import React from "react";
import Form from "./Form";
import Cardlist from "./Cardlist";
import ProfileData from "../JSON";
export default class App extends React.Component {
testData = ProfileData();
state = {
profiles: this.testData,
};
addNewProfile = (profile) => {
this.setState({
profiles: [...this.state.profiles, profile],
});
};
render() {
return (
<div>
<h4>GITHUB PROFILE CARDS</h4>
<Form onSubmit={this.addNewProfile} />
<Cardlist profiles={this.state.profiles} />
</div>
);
}
}
Form.js
import React from "react";
export default class Form extends React.Component {
state = { userName: "" };
handleSubmit = (event) => (
event.preventDefault(),
fetch(`https://api.github.com/users/${this.state.userName}`)
.then((response) => {
return response.json();
})
.then((data) => {
this.props.onSubmit(data);
})
);
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="username"
value={this.state.userName}
onChange={(event) =>
this.setState({ userName: event.target.value })
}
/>
<button>Add Card</button>
</form>
</div>
);
}
}
Cardlist.js
import React from "react";
import Card from "./Card";
export default class Cardlist extends React.Component {
profile = this.props.profiles.map((element) => (
<Card key={element.id} {...element} />
));
render() {
return (<div>{this.profile}</div>);
}
}
Card.js
import React from "react";
export default class Card extends React.Component {
render() {
return(
<div style = {{display: 'flex'}}>
<img src = {this.props.avatar_url} style = {{width: '100px', height: '100px'}}/>
<div>
<h6>{this.props.name}</h6>
<h6>{this.props.name}</h6>
</div>
</div>
);
}
}
component tree before
array before
component tree after
array after
You can change your CardList component to:
import React from "react";
import Card from "./Card";
export default class Cardlist extends React.Component {
render() {
return (
this.props.profiles.map((element) => (
<Card key={element.id} {...element} />
))
);
}
}
The problem with your CardList component is that you have a class property (profile) which is initialized when the CardList component is created but the class property does not update its value when the props is updated.
PS: you can also convert your class property into a function
profile = () => (
this.props.profiles.map((element) => (
<Card key={element.id} {...element} />
))
);
and the on render
render() {
return (<div>{this.profile()}</div>);
}
In Cardlist.js, instead of using a class variable, directly return the mapped elements.
import React from "react";
import Card from "./Card";
export default class Cardlist extends React.Component {
render() {
return this.props.profiles.map((element) => (
<Card key={element.id} {...element} />
));
}
}
Working demo on Codesandbox.
Although your CardList is received a new props from parent and then get rerender, this.profile outside your render function, so it never get updated. Just put it inside your render function and it works fine
class Cardlist extends React.Component {
render() {
const profile = this.props.profiles.map((element) => (
<Card key={element.id} {...element} />
));
return <div>{profile}</div>;
}
}
codesanbox
I am trying to make a movie search function using React, Axios, and movieDB API. The functionality I am trying to implement right now is typing in a movie into the search bar and clicking the submit button will return the movie title as an H1 element.
My onClick function does not work: <button onClick={(e)=>clickHandler()}>submit</button>
componentDidMount() will work only when the page refreshes and you cannot search for anything as the submit button is broken.
I am not sure how to implement this, but I would also not mind if I could get it to search by hitting enter instead of using a button, whichever is easier.
Here is my code so far.
App.js
import React from "react"
import Movielist from './components/Movielist'
function App() {
return (
<div>
<input type="search" id="search" />
<button onClick={(e)=>clickHandler()}>submit</button>
<h1 id="title">title</h1>
<Movielist />
</div>
)
}
export default App
Movielist.js
import React from 'react';
import axios from 'axios';
export default class Movielist extends React.Component {
state = {
title: ""
}
componentDidMount() {
const API_KEY = '***********************';
const query = document.getElementById('search').value;
axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${query}`)
.then(res => {
const title = res.data['results'][0]['title'];
this.setState({ title });
})
}
render() {
return (
<h1>{this.state.title}</h1>
)
}
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);
import React from 'react';
import axios from 'axios';
export default class Movielist extends React.Component {
state = {
title: ""
}
clickHandler = (event) => {
if (event.keyCode === 13) {
const query = event.target.value;
const API_KEY = '***********************';
axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${query}`)
.then(res => {
const title = res.data['results'][0]['title'];
this.setState({ title });
})
}
}
render() {
return (
<input type="search" id="search" onKeyDown={event => this.clickHandler(event)} />
<h1>{this.state.title}</h1>
)
}
}
You should call API to get the movie list after hitting the button, then pass the data that you've got to Movielist. Try this:
In App.js:
import React from "react"
import axios from 'axios'
import Movielist from './components/Movielist'
function App() {
const [movieList, setMovieList] = React.useState([])
const handleOnSubmit = () => {
const API_KEY = '***********************';
const query = document.getElementById('search').value;
axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${query}`)
.then(res => {
const title = res.data['results'][0]['title'];
setMovieList(res.data['results'])
})
}
return (
<div>
<input type="search" id="search" />
<button onClick={handleOnSubmit}>submit</button>
<h1 id="title">title</h1>
<Movielist movieList={movieList}/>
</div>
)
}
export default App
In Movielist.js:
import React from 'react';
const Movielist = ({movieList}) => {
return (
<div>
{
movieList.map(movie => <h1 key={movie.key}>{movie.title}</h1>)
}
<div/>
)
}
}
export default Movielist
import React, {useState} from "react"
import axios from 'axios';
import Movielist from './components/Movielist'
const [title, setTitle] = useState("")
const API_KEY = '***********************'
function App() {
const clickHandler = () => {
const query = document.getElementById('search').value;
axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${query}`)
.then(res => {
const title = res.data['results'][0]['title'];
setTitle(title);
})
}
return (
<div>
<input type="search" id="search" />
<button onClick={clickHandler}>submit</button>
<h1 id="title">title</h1>
<Movielist title={title} />
</div>
)
}
export default App
just move call api handle to your onclik func then pass title props to movie list
If you want to query the API after user push submit button. You should put your call to API in the call handler, then pass the state from App to MovieList as props
export class App extends React.Component {
state = {
title: ""
}
clickHandler() {
const API_KEY = '***********************';
const query = document.getElementById('search').value;
axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${query}`).then(res => {
const title = res.data['results'][0]['title'];
this.setState({ title });
});
}
render() {
return (
<div>
<input type="search" id="search" />
<button onClick={(e)=>clickHandler()}>submit</button>
<h1 id="title">title</h1>
<Movielist list={this.state.title}/>
</div>
)
}
}
export class MovieList extends React.Component {
render() {
<h1>{this.props.title}</h1>
}
}
Alternatively, you can wrap the input in a element and use onSubmit + evt.preventDefault() instead, by doing so you can handle button click and pressing "Enter" to submit.
I'm getting props.handleChange is not a function when running the following code. I'm trying to update the state when the checkbox is clicked. The field that is check box is called myNetwork. I thought that when NetworkArray component, which is a parent of Card component, would have access to the functions and state in App? But this is my first React App. Please, what am I doing wrong?
App.JS
import React, {Component} from 'react';
import SignUp from './components/SignUp';
import NetworkArray from './components/NetworkArray';
import {network} from './NetworkData'
import './App.css';
import 'tachyons';
class App extends Component {
constructor() {
super()
this.state = {
network: network,
}
this.handleChange=this.handleChange.bind(this);
}
handleChange(id) {
this.setState(prevState => {
const updatedNetwork = prevState.network.map(netw => {
if (netw.id===id) {
netw.myNetwork = !netw.myNetwork
}
return netw
})
return {
network:updatedNetwork
}
})
}
render() {
return (
<div>
<NetworkArray
network={network}
handleChange = {this.handleChange} />
</div>
);
}
}
export default App;
Card.js
import React from 'react';
const Card = (props) => {
return(
<div className = 'bg-light-green dib br3 pa3 ma2 grow shadow-5'>
<div>
<h3>{props.name}</h3>
<p>{props.company}</p>
<p>{props.phone}</p>
<p>{props.email}</p>
<p>{props.city}</p>
</div>
<div>
MyNetwork
<input
type = "checkbox"
checked={props.myNetwork}
onChange={()=> props.handleChange(props.id)}
/>
</div>
</div>
)
}
export default Card;
NetworkArray.js
import React, {Component} from 'react';
import Card from './Card';
const NetworkArray = ({network}) => {
const cardComponent = network.map((user,i) => {
return(
<Card
key = {network[i].id}
name = {network[i].firstName + ' ' + network[i].lastName}
company = {network[i].company}
phone= {network[i].phone}
email={network[i].email}
city = {network[i].city}
/>
)
})
return (
<div>
{cardComponent}
</div>
)
}
export default NetworkArray;
You passed the function from App component to NetworkArray component, but not to Card component.
const NetworkArray = ({network, handleChange}) => {
...
<Card
handleChange={handleChange}
...
/>
}
I'm working on simple form element using react-js.
There are three component:
App
TakeInput
Index
problem is when user put text in input field setState() function not work properly and data not updated. For testing purpose when i'm placing console.log in app js component it shows undefined on console. anyone sort this please. I want to console the updated data when state update
App.js
import React, { Component } from 'react';
import InputField from './TakeInput';
class App extends Component {
state = {
userInp : '',
outText : ''
}
handlechanger2 = (v) => {
this.setState( () => ({
userInp: v,
}))
console.log(this.userInp);
}
render() {
return (
<div className="App">
<InputField changingVal={this.handlechanger2}/>
</div>
);
}
}
export default App;
TakeInput.JS
import React, { Component } from 'react';
class TakeInput extends Component{
state={
txt: ''
}
handlerChange = (e)=>{
const { changingVal } = this.props;
const v = document.getElementById("userInput").value;
changingVal(v);
// console.log(e.target.value);
this.setState({ txt: e.target.value })
}
render(){
return(
<input type="text" name="userInput" id="userInput" placeholder="Please Enter Text" onChange={this.handlerChange} value={this.txt}/>
)
}
}
export default TakeInput;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
it is about you are developing wrong way. I think you text input should be at your parent component
To read from state you should use this.state.abc
import React, { Component } from 'react';
class TakeInput extends Component{
handlerChange = (e)=>{
this.props.onChange(e.target.value);
}
render(){
return(
<input type="text" name="userInput" placeholder="Please Enter Text" onChange={this.handlerChange} value={this.props.txt}/>
)
}
}
export default TakeInput;
import React, { Component } from 'react';
import InputField from './TakeInput';
class App extends Component {
state = {
userInp : '',
outText : ''
}
handlechanger2 = (v) => {
this.setState( () => ({
userInp: v,
}))
console.log(this.state.userInp);
}
render() {
return (
<div className="App">
<InputField txt={this.state.userInp} onChange={this.handlechanger2}/>
</div>
);
}
}
export default App;
You're trying to console.log this.userInp. It should be this.state.userInp.
Also to see the update right after the last set state, you can do the following:
handlechanger2 = (v) => {
this.setState( () => ({
userInp: v,
}), function(){ console.log(this.state.userInp);}) // set a callback on the setState
}
I've been learning redux & react and am building a todo list. I've been reading and looking at different articles but cant figure out what I'm missing in my setup.
Currently you can add a todo via the input. On pushing enter it sends a addTodo action with the todo text.
I'm expecting the reducer to see the action type and update the state but it never does. What am I missing?
index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './reducer.js';
import TodoList from './containers/container.js';
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<TodoList />
</Provider>,
document.getElementById('app'));
actions.js
var uuid = require('node-uuid');
export function addTodo(text) {
console.log('action addTodo', text);
return {
type: 'ADD_TODO',
payload: {
id: uuid.v4(),
text: text
}
};
}
TodoListComponent.jsx
import React from 'react';
import TodoComponent from './TodoComponent.jsx';
import { addTodo } from '../actions/actions.js'
export default class TodoList extends React.Component {
render () {
const { todos } = this.props;
return (
<div>
<input type='text' placeholder='Add todo' onKeyDown={this.onSubmit} />
<ul>
{todos.map(c => (
<li key={t.id}>
<TodoComponent todo={t} styleName='large' />
</li>
))}
</ul>
</div>
)
}
onSubmit(e) {
const input = e.target;
const text = input.value;
const isEnterKey = (e.which === 13);
if (isEnterKey) {
input.value = '';
addTodo(text);
}
}
}
TodoComponent.jsx
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './style.css';
export default class TodoComponent extends React.Component {
render () {
const { todo } = this.props;
return (
<div styleName='large'>{todo.text}</div>
)
}
}
export default CSSModules(TodoComponent, styles);
container.js
import { connect } from 'react-redux';
import TodoList from '../components/TodoListComponent.jsx';
import { addTodo } from '../actions/actions.js';
const mapStateToProps = (state) => {
return {
todos: state
}
};
const mapDispatchToProps = (dispatch) => {
return {
addTodo: text => dispatch(addTodo(text))
}
};
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
reducer.js
import { List, Map } from 'immutable';
const init = List([]);
export default function(todos = init, action) {
console.log('reducer action type', action.type);
switch(action.type) {
case 'ADD_TODO':
console.log('ADD_TODO');
return todos.push(Map(action.payload));
default:
return todos;
}
}
In your TodoListComponent you are importing your actions directly from your actions file, but in fact you want to use the action that you map to dispatch and pass as property in the container. That explains why you see logs from the actions, but not from reducer, as the action is never dispatched to the store.
So your TodoListComponent should be:
import React from 'react';
import TodoComponent from './TodoComponent.jsx';
export default class TodoList extends React.Component {
render () {
const { todos } = this.props;
return (
<div>
<input type='text' placeholder='Add todo' onKeyDown={this.onSubmit} />
<ul>
{todos.map(c => (
<li key={t.id}>
<TodoComponent todo={t} styleName='large' />
</li>
))}
</ul>
</div>
)
}
onSubmit(e) {
const input = e.target;
const text = input.value;
const isEnterKey = (e.which === 13);
if (isEnterKey) {
input.value = '';
this.props.addTodo(text);
}
}
}
import React from 'react';
import TodoComponent from './TodoComponent.jsx';
export default class TodoList extends React.Component {
render () {
const { todos } = this.props;
return (
<div>
<input type='text' placeholder='Add todo' onKeyDown={this.onSubmit.bind(this)} />
<ul>
{todos.map(c => (
<li key={t.id}>
<TodoComponent todo={t} styleName='large' />
</li>
))}
</ul>
</div>
)
}
onSubmit(e) {
// use the addTodo passed via connect
const { addTodo } = this.props;
const input = e.target;
const text = input.value;
const isEnterKey = (e.which === 13);
if (isEnterKey) {
input.value = '';
addTodo(text);
}
}
}