In the renderList(), I have a delete button that will delete the content once it is clicked. I am not sure where to put the setState so I put it inside on the onClick(). This doesn't work. I would like to know if I am doing this correct or if there is a better way to solve this.
onClick Function
onClick={() => {
this.props.deleteBook(list.book_id);
this.setState({delete: list.book_id});
}}>
React.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { selectUser } from '../actions/index.js';
import { deleteBook } from '../actions/index.js';
import _ from 'lodash';
class myPage extends Component {
constructor(props) {
super(props);
this.state = {
delete: 0
}
}
componentWillMount() {
this.props.selectUser(this.props.params.id);
}
renderList() {
return this.props.list.map((list) => {
return (
<li className='book-list'
key={list.book_id}>
{list.title}
<button
value={this.state.delete}
onChange={this.onClickChange}
onClick={() => {
this.props.deleteBook(list.book_id);
this.setState({delete: list.book_id});
}}>
Delete
</button>
</li>
);
})
}
render() {
const {user} = this.props;
const {list} = this.props;
if(user) {
return(
<div>
<h2>Date Joined: {user.user.joined}</h2>
<h1>My Page</h1>
<h2>Username: {user.user.username}</h2>
<div>My Books:
<h1>
{this.renderList()}
</h1>
</div>
</div>
)
}
}
}
function mapStateToProps(state) {
return {
user: state.user.post,
list: state.list.all
}
}
export default connect(mapStateToProps, { selectUser, deleteBook })(myPage);
Based on your use of mapStateToProps, seems like you are using Redux. Your list of books comes from the Redux store as props which is external to the component.
You do not need this.state.delete in the component. As state is managed by Redux, it seems like the bug is in your Redux code and not React code. Look into the reducers and ensure that you are handling the delete item action correctly.
Related
i have a random list of 10 dogs and fetching it from api https://dog.ceo/api/breeds/image/random/10 and with every refresh i get another 10 dogs list so my question is how do i make favorite list out of it and show it on another page. I dont want to use Localstorage or redux or context api.
DogHome.js
import "bootstrap/dist/css/bootstrap.min.css";
import "../../Pages/Pages.css";
import DogList from "../Dog/DogList";
import React, { Component } from "react";
class DogHome extends Component {
constructor(props) {
super(props);
this.state = {
dogs: [],
};
}
async componentDidMount() {
try {
const url = "https://dog.ceo/api/breeds/image/random/10";
const response = await fetch(url);
const data = await response.json();
this.setState({ dogs: data.message });
} catch (e) {
console.error(e);
}
}
render() {
return (
<div className="home">
<DogList dogs={this.state.dogs} />
</div>
);
}
}
export default DogHome;
DogList.js
import React from "react";
import Dog from "./Dog";
import "../../Pages/Pages.css";
const DogList = (props) => {
const dogsArray = props.dogs.map((dogURL, index) => {
return <Dog key={index} url={dogURL} />;
});
return (
<>
<div className="doglist">{dogsArray}</div>
</>
);
};
export default DogList;
and lastly here i have a favorite button in every dog image which i want to make favorite on click.
Dog.js
import React from "react";
import { Button } from "react-bootstrap";
import "../../Pages/Pages.css";
const Dog = (props) => {
return (
<div id="child">
<img
style={{ width: 300, height: 300 }}
src={props.url}
alt="ten dogs list"
/>
<Button >Favorite</Button>
</div>
);
};
export default Dog;
You say don't want to use those APIs but you need a method to store favorites. How are you going to decide which are the favorites? How else are you planning to store the values of favorite dogs? You can use a database or a flat file. But you might as well take advantage of some of the React state management if that's your goal.
I know this is a really basic thing, but for some reason my mind is just running a blank right now on what Im doing wrong. So basically all Im trying to do is click a button and show the value go up by 1 everytime I click it. Im trying to have my Increment button as a child and then pass the new value up to the parent.
This is my app.js (parent)
import React from 'react';
import IncrementButton from './increment-button';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
increment(){
this.setState({
count:this.state.count+1
})
}
render() {
return (
<div className="parent">
<div className="count">
Number of clicks: {this.state.count}
</div>
<IncrementButton count={this.increment}/>
</div>
);
}
}
This is my increment-button component
import React from 'react';
export default function IncrementButton(props) {
return <button onClick={() => props.count}>Increment</button>;
}
Right now Im not seeing any change for "Number of clicks:"
You gotto call the count() since it is a function -
<button onClick={() => props.count()}>Increment</button>;
props.count is a function, invoke it.
import React from 'react';
export default function IncrementButton(props) {
return <button onClick={() => props.count()}>Increment</button>;
}
Also for your app.js, you need to bind the scope for this.increment, so use arrow function instead.
import React from 'react';
import IncrementButton from './increment-button';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
increment(){
this.setState({
count:this.state.count+1
})
}
render() {
return (
<div className="parent">
<div className="count">
Number of clicks: {this.state.count}
</div>
<IncrementButton count={() => this.increment()}/>
</div>
);
}
}
I'm new to react and trying to create a ecommerce website. For the navigation and category items, I have used a endpoint url.
http://149.129.128.3:3737/search/resources/store/1/categoryview/#top?depthAndLimit=-1,-1,-1,-1
and the screenshot of response is given below
The problem which I'm facing is that when I click on the categories like Apparel, Electronics, upto the first level of category, I'm able to render it.
But under that subsection, there is another category, which I'm not able to render(E.g under Girls there is shoes, pants etc) and the code is given below
topNavigation.js
import React, { Component } from 'react';
import axios from 'axios';
import SubMenu from './subMenu';
class Navigation extends Component {
state = {
mainCategory: []
}
componentDidMount() {
axios.get('http://localhost:3030/topCategory')
.then(res => {
console.log(res.data.express);
this.setState({
mainCategory: res.data.express.catalogGroupView
})
})
}
render() {
const { mainCategory } = this.state;
return mainCategory.map(navList => {
return (
<ul className="header">
<li key={navList.uniqueID}>
<a className="dropbtn ">
{navList.name}
<ul className="dropdown-content">
<SubMenu below={navList.catalogGroupView}/>
</ul>
</a>
</li>
</ul>
)
})
}
}
export default Navigation;
subMenu.js
import React, { Component } from 'react';
import SubListMenu from './subListMenu';
class SubMenu extends Component {
render() {
const {below} = this.props;
return below.map(sub => {
return (
<div>
<li key={sub.uniqueID}> <a>{sub.name}</a></li>
<SubListMenu subBelow={this.props}/>
</div>
)
})
}
}
export default SubMenu;
subListMenu.js
import React, { Component } from 'react';
class SubListMenu extends Component {
render() {
const {subBelow} = this.props;
return subBelow.map(sub => {
return (
<li key={sub.uniqueID}> <a>{sub.name}</a></li>
)
})
}
}
export default SubListMenu;
Can someone please help me on this. I'm not able to figure it out. I don't know where my code is getting wrong. Or if somebody could atleast give an insight how to proceed further.
Try to change this line:
<SubListMenu subBelow={this.props}/>
to this:
<SubListMenu {...this.props}/>
or to this
<SubListMenu subBelow={this.props.below}/>
You are passing the whole props object to subBelow, so you are trying to map through an object, but Object.prototype.map is not a function.
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
I'm building a react application where the header changes based on the routes, but also depending of the state of other components.
I'm looking for a way to control the header from child components.
For example, I would like that when I click on a button in the main page the header would append new components.
Is there a way to achieve this while avoiding multiple if statements in the header ?
Have a variable in your state which contains the content to be appended to the header. React takes care of reloading all the components when there is a change in the state.
Ex - App.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import { appendHeaderDemo } from 'redux/demo.js'
class ChildComponent1 extends Component {
render() {
return <h3>Child component 1 added to header</h3>
}
};
class ChildComponent2 extends Component {
render() {
return <h3>Child component 2 added to header</h3>
}
};
class App extends Component {
render() {
const { dispatch } = this.props
return (
<div className='container'>
<h1> This is my header {this.props.appendToHeader} </h1>
{ this.props.appendToHeader == 'Button clicked' &&
<ChildComponent1 />
}
{ this.props.appendToHeader == 'Some Other State' &&
<ChildComponent2 />
}
<button type="button" onClick={ () => onButtonClick() }> Change Header Content </button>
<div className='container'>
{this.props.children}
</div>
</div>
);
};
}
function mapStateToProps(state) {
const { appendToHeader } = state
return {
appendToHeader
}
}
export default connect(mapStateToProps, { onButtonClick: appendHeaderDemo })(App)
redux/demo.js -
export const CHANGE_HEADER_CONTENT = 'CHANGE_HEADER_CONTENT'
const initialState = {
appendToHeader: ''
};
// Reducer
export default function appendHeader(state = initialState, action) {
switch (action.type) {
case CHANGE_HEADER_CONTENT:
return Object.assign({}, state, {
appendToHeader: 'Button clicked'
})
default:
return state
}
}
// Actions
export function appendHeaderDemo() {
return {
type: CHANGE_HEADER_CONTENT
}
}
Dispatch function appendHeaderDemo can be called from any child and the corresponding changes will be reflected in the header (if there is a change in the state attribute appendToHeader)