First of all I know a lot of stuff in this code snippet is off, I know. I'm only trying to address the question here.
For some reason my app says that it cannot read property 'todo' of undefined, and highlights todos: [...state.todos].
Am I not importing something that I should be here?
Form.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import uuidv1 from 'uuid';
import { addTodo } from '../actions';
import TodoInput from './todo-input';
import TodoList from './TodoList';
const mapDispatchToProps = dispatch => {
return {
addTodo: todo => dispatch(addTodo(todo))
};
};
const mapStateToProps = (state) => ({
todos: [...state.todos]
})
class ConnectedForm extends Component {
constructor(props){
super(props);
this.state = {
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleToggle = this.handleToggle.bind(this);
};
handleChange = (e) => {
e.preventDefault();
this.setState({
inputValue: e.target.value
});
}
handleSubmit = (e) => {
e.preventDefault();
const { inputValue} = this.state;
const id = uuidv1();
this.props.addTodo({inputValue, id});
this.setState({inputValue: ''});
}
handleToggle (e) {
const id = parseInt(e.target.id);
this.setState((prevState) => ({
todos: prevState.todos.map(todo => todo.id === id ? {...todo, done: !todo.done} : todo)
}));
console.log(e.target);
}
render() {
const { inputValue } = this.state;
return (
<div className='form-group'>
<TodoInput
value={inputValue}
onChange={this.handleChange}
onSubmit={this.handleSubmit}
/>
<TodoList />
</div>
);
}
}
const Form = connect(mapStateToProps, mapDispatchToProps) (ConnectedForm);
export default Form;
TodoList.js
import React, { Component } from 'react';
import TodoItem from './TodoItem';
import { removeTodo, toggleComplete } from '../actions';
import { connect } from 'react-redux';
const mapDispatchToProps = dispatch => {
return {
removeTodo: id => dispatch(removeTodo(id)),
toggleComplete: isDone => dispatch(toggleComplete(isDone))
};
};
const mapStateToProps = state => {
return {todos: [...state.todos]};
};
class List extends Component {
render() {
const mappedTodos = this.props.todos.map((todo, index) => (
<TodoItem
todo={todo}
title={todo.title}
key={index}
removeHandler={this.props.removeTodo}
toggleComplete={this.props.toggleComplete}
/>
));
return (
mappedTodos
);
}
}
const TodoList = connect(mapStateToProps, mapDispatchToProps) (List)
export default TodoList;
Reducers
import { ADD_TODO, REMOVE_TODO, TOGGLE_COMPLETE } from '../constants/action-types';
import uuidv1 from 'uuid';
const initialState = {
todos: []
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [...state.todos,
{
title: action.payload.inputValue,
id: uuidv1(),
createdAt: Date(),
priority: '',
deadline: '',
isComplete: false
}]
}
case REMOVE_TODO:
return {
...state,
todos: [...state.todos.filter(todo => todo.id !== action.payload)]
}
case TOGGLE_COMPLETE:
return (
console.log(action.payload)
)
default:
return state;
}
}
export default rootReducer;
Related
I am trying to update an element from an array by adding an object as a property like shown in this picture
When a user clicks on a single node button, a modal appears the user fills the form and then it is addes as a property for this node.
But for some reason I get this type error that says that the updateElement is not a function.
BTW, I am using Redux & react-flow-renderer libraries.
Reducer
import * as types from '../actions/types';
const initialState = {
elements: []
};
const flow = (state = initialState, action) => {
switch (action.type) {
case types.UPDATE_ELEMENT:
return {
...state,
elements: state.elements.map((e) => {
if (e.id === action.payload.id) {
e = {
...e,
options: action.payload.options,
};
}
return e;
}),
};
default:
return state;
}
};
export default flow;
Action
import { UPDATE_ELEMENT } from './types';
export const updateElement = (data) => (dispatch) => {
dispatch({
type: UPDATE_ELEMENT,
payload: data,
});
};
Node modal
import React, { useState } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { updateElement } from '../../../../redux/actions/flow';
const VPCNodeModal = (props, { updateElement }) => {
const [formData, setFormData] = useState({
instance: '',
});
// options
const { instance } = formData;
const onFormChange = (e) =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmitForm = () => {
const update = {
id: selectedElement.id,
options: formData,
};
updateElement(update);
};
return (
<>
<Modal {...props}>
<form
onSubmit={(e) => {
e.preventDefault();
onSubmitForm();
}}
>
<label>
<span> Instance name:</span>
<input
type='text'
name='instance'
value={instance}
onChange={onFormChange}
/>
</label>
<button type='submit'>Submit</button>
</form>
</Modal>
</>
);
};
VPCNodeModal.propTypes = {
updateElement: PropTypes.func.isRequired
};
export default connect(null, { updateElement })(VPCNodeModal);
Issue is while receiving the props.
change
const VPCNodeModal = (props, { updateElement }) => {
to
const VPCNodeModal = (props) => {
const { updateElement } = props;
updateElement is a props was passes in VPCNodeModal. So you should update like this with spread operator
const VPCNodeModal = ({ updateElement, ...props }) => {
I have no idea How to store the react js state into localstorage.
import React, { Component } from 'react'
import './App.css';
import { auth,createUserProfileDocument } from './firebase/firebase.utils'
import { TodoForm } from './components/TodoForm/TodoForm.component'
import {TodoList} from './components/TodoList/TodoList.component'
import {Footer} from './components/footer/footer.component'
import Header from '../src/components/header/header.component'
import {Redirect} from 'react-router-dom'
import {connect} from 'react-redux'
import {setCurrentUser} from './redux/user/user.actions'
export class App extends Component {
constructor(props) {
super(props)
this.input=React.createRef()
this.state = {
todos:[
{id:0, content:'Welcome Sir!',isCompleted:null},
]
}
}
todoDelete = (id) =>{
const todos = this.state.todos.filter(todo => {
return todo.id !== id
})
this.setState({
todos
})
}
toDoComplete = (id,isCompleted) =>{
console.log(isCompleted)
var todos = [...this.state.todos];
var index = todos.findIndex(obj => obj.id === id);
todos[index].isCompleted = !isCompleted;
this.setState({todos});
console.log(isCompleted)
}
addTODO = (todo) =>{
todo.id = Math.random()
todo.isCompleted = true
let todos = [...this.state.todos, todo]
this.setState({
todos
})
}
unsubscribeFromAuth = null;
componentDidMount() {
const { setCurrentUser } = this.props;
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapShot => {
setCurrentUser({
id: snapShot.id,
...snapShot.data()
});
});
}
setCurrentUser(userAuth);
});
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
return (
<div className='App'>
<Header />
<TodoForm addTODO={this.addTODO} />
<TodoList
todos={this.state.todos}
todoDelete={ this.todoDelete}
toDoComplete={ this.toDoComplete}
/>
<Footer/>
</div>
)
}
}
const mapStateToProps = ({ user }) => ({
currentUser: user.currentUser
});
const mapDispatchToProps = dispatch => ({
setCurrentUser: user => dispatch(setCurrentUser(user))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
in my input Form
import './TodoForm.style.css'
export class TodoForm extends Component {
constructor(props) {
super(props)
this.state = {
content : ''
}
}
handleChange = (e) =>{
this.setState({
content: e.target.value
})
}
handleSubmit =(e) =>{
e.preventDefault();
this.props.addTODO(this.state);
this.setState({
content: ''
})
}
render() {
return (
<div className='inputTask'>
<form onSubmit={ this.handleSubmit}>
<input
className="textBox"
type='text'
onChange={ this.handleChange}
value={this.state.content}
placeholder='what you want to do ...'
/>
</form>
</div>
)
}
}
export default TodoForm
I have no idea How to store the react js state into localstorage.
i searched on internet but unable to find the exact solution all the codes that i think is necessary post.
You can use reactLocalStorage to save any data in local storage
import {reactLocalStorage} from 'reactjs-localstorage';
reactLocalStorage.set('var', true);
reactLocalStorage.get('var', true);
reactLocalStorage.setObject('var', {'test': 'test'});
reactLocalStorage.getObject('var');
reactLocalStorage.remove('var');
reactLocalStorage.clear();
Read out the localStorage item in the componentDidMount callback. Simply read the item you want to get, check if it exists and parse it to a usable object, array or datatype that need. Then set the state with the results gotten from the storage.
And to store it, simply handle it in an event handler or helper method to update both the state and the localStorage item.
class ExampleComponent extends Component {
constructor() {
super();
this.state = {
something: {
foo: 'bar'
}
}
}
componentDidMount() {
const storedState = localStorage.getItem('state');
if (storedState !== null) {
const parsedState = JSON.parse(storedState);
this.setState({ something: parsedState });
}
}
clickHandler = (event) => {
const value = event.target.value;
const stringifiedValue = JSON.stringify(value);
localStorage.setItem('state', stringifiedValue);
this.setState({ something: value });
}
render() {
return (
<button onClick={clickHandler} value={this.state.something}>Click me</button>
);
}
}
Set data in localStorage
key-value pair :
localStorage.setItem('key_name',"value");
object
localStorage.setItem('key_name', JSON.stringify(object));
Remove data from localStorage
localStorage.removeItem('key_name');
Get data from localStorage
let data = localStorage.getItem('key_name');
object :
let data = JSON.parse(localStorage.getItem('key_name'));
clear localStorage (delete all data)
localStorage.clear();
Another newbie propblem. I want to post a post with my form. I have Post.js that looks like this:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import PostForm from './PostFormContainer';
export class Post extends Component {
static propTypes = {
posts: PropTypes.any,
fetchPosts: PropTypes.func,
sendPostData: PropTypes.func,
};
componentDidMount() {
const { fetchPosts } = this.props;
fetchPosts();
}
// onSubmit = (e, id, title, body) => {
// e.preventDefault();
// axios
// .post('https://jsonplaceholder.typicode.com/posts', {
// id,
// title,
// body,
// })
// .then(res =>
// this.setState({
// posts: [...this.state.posts, res.data],
// })
// );
// };
// onSubmit(e, id, title, body) {
// e.preventDefault();
// console.log('data');
// console.log('data', id);
// console.log('data', title);
// console.log('data', body);
// this.props.sendPostData(id, title, body);
// console.log('sendPostData', this.props.sendPostData(id, title, body));
// }
render() {
console.log('props', this.props);
const { posts } = this.props;
if (!posts.length) {
return (
<div>
<PostForm addPost={this.onSubmit} />
</div>
);
} else {
return (
<div>
<PostForm addPost={this.onSubmit} />
<br />
<div>
{posts.map(post => (
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</div>
))}
;
</div>
</div>
);
}
}
}
export default Post;
Where I have <PostForm addPost={this.onSubmit} />
My PostForm.js looks like this:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class PostForm extends Component {
///state = {
// title: '',
// body: '',
//};
static propTypes = {
posts: PropTypes.any,
// fetchPosts: PropTypes.func,
sendPostData: PropTypes.func,
};
//onChange = e => {
//this.setState({
// e.target.name zawsze będzie targetował pole z value i zmieniał jego stan
// [e.target.name]: e.target.value,
// });
//};
// onSubmit(e, id, title, body) {
// e.preventDefault();
// console.log('data');
// console.log('data', id);
// console.log('data', title);
// console.log('data', body);
// }
onSubmit(e, id, title, body) {
e.preventDefault();
console.log('data');
console.log('data', id);
console.log('data', title);
console.log('data', body);
// const post = {
// title,
// body,
// };
this.props.sendPostData(title, body);
// console.log('sendPostData', this.props.sendPostData(post));
}
render() {
console.log('props form', this.props);
const { title, body } = this.props;
return (
<div>
<h1> Add Post </h1>
<form onSubmit={e => this.onSubmit(e, title, body)}>
<div>
<label>Title: </label>
<input
type='text'
name='title'
value={title}
onChange={this.onChange}
/>
</div>
<div>
<label>Body: </label>
<textarea name='body' value={body} onChange={this.onChange} />
</div>
<button type='submit'>Submit</button>
</form>
</div>
);
}
}
export default PostForm;
Here I want to send this with my action.
I have two container files
PostFormContainer.js
import { connect } from 'react-redux';
import PostForm from './PostForm';
import { sendPost } from '../reducers/postReducers';
const mapStateToProps = state => ({
posts: state.posts,
});
const mapDispatchToProps = dispatch => ({
sendPostData: post => dispatch(sendPost(post)),
});
export default connect(mapStateToProps, mapDispatchToProps)(PostForm);
and PostContainer.js
import { connect } from 'react-redux';
import Post from './Post';
import { fetchFromApi } from '../reducers/postReducers';
const mapStateToProps = state => ({
posts: state.posts,
});
const mapDispatchToProps = dispatch => ({
fetchPosts: () => dispatch(fetchFromApi()),
// sendPostData: (id, title, body) => dispatch(sendPost({ id, title, body })),
});
export default connect(mapStateToProps, mapDispatchToProps)(Post);
and my reducer
import Axios from 'axios';
const reducerName = 'posts';
const createActionName = name => `/${reducerName}/${name}`;
/* action type */
const FETCH_POSTS = createActionName('FETCH_POSTS');
const SUBMIT_POST = createActionName('SUBMIT_POST');
/* action creator */
export const fetchStarted = payload => ({ payload, type: FETCH_POSTS });
export const submitPost = payload => ({ payload, type: SUBMIT_POST });
/* thunk */
export const fetchFromApi = () => {
return (dispatch, getState) => {
Axios.get('https://jsonplaceholder.typicode.com/posts?_limit=5').then(
res => dispatch(fetchStarted(res.data))
// console.log('res', res)
// console.log('res data', res.data)
);
};
};
export const sendPost = (postId, postTitle, postBody) => {
return (dispatch, getState) => {
Axios.post('https://jsonplaceholder.typicode.com/posts', {
id: postId,
title: postTitle,
body: postBody,
}).then(res => {
dispatch(submitPost(res.data));
});
};
};
/* reducer */
export default function reducer(state = [], action = {}) {
switch (action.type) {
case FETCH_POSTS:
return action.payload;
case SUBMIT_POST: {
return {
...state,
data: action.payload,
};
}
default:
return state;
}
}
Right now my console.logs shows that all my data is undefined. Not sure what the I am missing, but I can't solve this.
Here is also my stro.js
import { combineReducers, applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import postReducer from './reducers/postReducers';
const initialState = {
posts: {
data: {},
},
};
const reducers = {
posts: postReducer,
};
Object.keys(initialState).forEach(item => {
if (typeof reducers[item] == 'undefined') {
reducers[item] = (state = null) => state;
}
});
const combinedReducers = combineReducers(reducers);
const store = createStore(
combinedReducers,
initialState,
composeWithDevTools(applyMiddleware(thunk))
);
export default store;
Your PostForm element uses props title and body, but the place where you use PostForm doesn't send it a body or title prop.
I don't know your particular use case, but in React/Redux there are two ways to send a property to an element:
<PostForm body={this.state.postFormBody} title={this.state.postFormTitle} />
Or by using your Redux connector, mapStateToProps function, and returning an object with 'body' and 'title' keys that match something in your Redux store
i'm calling a api and getting my data and setting it to redux state successfully but when i'm mapping it to my component it's first returning undefined then it's calling the the api but i'm using redux-thunk for it
this is my header component
export class header extends Component {
componentDidMount() {
this.props.addUpcomingMovies();
}
render() {
const Movies = this.props.popularMovies.map(movie => (
<div key={movie.id} className="header-slide-container">
<div className="header-slide">
<img
src={`https://image.tmdb.org/t/p/original${movie.poster_path}`}
alt=""
/>
{(() => {
if (!movie.title) {
return null;
}
return <Title movie={movie} />;
})()}
{(() => {
if (!movie.original_title) {
return null;
}
return <OriginalTitle movie={movie} />;
})()}
{(() => {
if (!movie.original_name) {
return null;
}
return <OriginalName movie={movie} />;
})()}
</div>
</div>
));
return (
<div className="header">
<div className="header-container">
<div className="header-slider-wrapper">
{console.log(this.props.popularMovies)}
{<Movies />}
</div>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
popularMovies: state.upComingMovies.movies
});
export default connect(
mapStateToProps,
{ addUpcomingMovies }
)(header);
this is my upcoming movie reducer
const upComingMovie = (state = [], action) => {
switch (action.type) {
case "ADD_UPCOMING_MOVIES":
console.log('reducers');
return {
...state,
movies:action.payload
}
default:
return state;
}
};
export default upComingMovie;
combining reducers
import upComingMovie from './upcomingMovies';
import {combineReducers} from 'redux';
const allReducers = combineReducers({
upComingMovies:upComingMovie
})
export default allReducers
upcoming movie action
export const addUpcomingMovies = () => dispatch => {
console.log("fetching");
fetch("https://api.themoviedb.org/3/trending/all/week?api_key=25050db00f2ae7ba0e6b1631fc0d272f&language=en-US&page=1")
.then(res => res.json())
.then(movies =>
dispatch({
type: "ADD_UPCOMING_MOVIES",
payload: movies
})
);
};
this is my store
import { createStore, applyMiddleware,compose } from 'redux';
import thunk from 'redux-thunk';
import allReducers from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(allReducers,initialState,compose(applyMiddleware(...middleware), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()));
export default store;
Your reducer is not object, your reducer is an array
const upComingMovie = (state = [], action) => {}
// ____________________________^
But your are using object
case "ADD_UPCOMING_MOVIES":
return { // <--------------- this is an object notation
...state,
movies:action.payload
}
Solution
const initialState = {
movies: []
}
const upComingMovie = (state = initialState , action) => {}
Your initialState is wrong, it'd be {movies: []}:
const initialState = { movies: []};
// ...
const upComingMovie = (state = initialState, action) => {
// ...
Also use the same initial state in your store.
In my index.js the addCoin action is working.
import { addCoin } from './reducer/portfolio/actions'
const element = document.getElementById('coinhover');
const store = createStore(reducer, compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
));
store.dispatch(addCoin('bitcoin'));
When store.dispatch is called I can see the updated state here.
However I do not want to call dispatch actions from my index.js, but from within my components.
My SearchCoin component:
import React from 'react'
import { connect } from 'react-redux'
import * as R from 'ramda'
import * as api from '../../services/api'
import { addToPortfolio, findCoins } from '../../services/coinFactory'
import { addCoin } from '../../reducer/portfolio/actions'
const mapDispatchToProps = (dispatch) => ({
selectCoin(coin) {
return () => {
dispatch(addCoin(coin))
}
}
});
class SearchCoin extends React.Component {
constructor(props) {
super(props)
this.state = {
searched: []
};
// console.log('props', props);
this.close = this.close.bind(this);
}
componentDidMount() {
this.coinInput.focus();
this.handleChange = this.handleChange.bind(this);
this.clickCoin = this.clickCoin.bind(this);
}
handleChange() {
const text = document.getElementById('coin-search').value;
const search = (text) => this.setState({ searched: findCoins(text) });
const clearSearch = () => this.setState({ searched: [] });
text.length > 1 ? search(text) : clearSearch();
}
clickCoin(coin) {
console.log('clickCoin', coin);
// api.getCoin(coin.id).then((res) => {
// const apiCoin = R.head(res.data);
// addToPortfolio(apiCoin);
// });
this.props.selectCoin(coin);
this.props.closeSearch();
}
close() {
this.props.closeSearch();
}
render() {
const searched = this.state.searched.map((coin) => {
return (
<li key={ coin.id } onClick={ ()=> this.clickCoin(coin) }>
<div className="coin-logo">
<img src={ coin.logo }/>
</div>
<span>{ coin.name }</span>
</li>
);
});
return (
<div id="search-coin-component">
<input type="text"
id="coin-search"
className="coin-search-input fl"
placeholder="Search"
onChange={ ()=> this.handleChange() }
ref={ (input) => { this.coinInput = input; } } />
<div className="icon-cancel-outline fl" onClick={ this.close }></div>
<div className="coin-select">
<ul>
{ searched }
</ul>
</div>
</div>
)
}
}
export default connect(null, mapDispatchToProps)(SearchCoin)
This is the onClick:
<li key={ coin.id } onClick={ ()=> this.clickCoin(coin) }>
At the bottom of the file I am using connect to add mapDispatchToProps
export default connect(null, mapDispatchToProps)(SearchCoin)
Here is the class method clickCoin which calls this.props.selectCoin
clickCoin(coin) {
console.log('clickCoin', coin);
this.props.selectCoin(coin);
this.props.closeSearch();
}
Finally selectCoin
import { addCoin } from '../../reducer/portfolio/actions'
const mapDispatchToProps = (dispatch) => ({
selectCoin(coin) {
return () => {
dispatch(addCoin(coin))
}
}
});
However when I click the button it seems like the dispatch is not fired as nothing happens to the redux state.
import * as R from 'ramda'
import * as api from '../../services/api'
import { addToPortfolio } from '../../services/coinFactory'
export const ADD_COIN = 'ADD_COIN'
export function addCoin(coin) {
console.log('addCoin', coin);
return dispatch =>
api.getCoin(coin)
.then((res) => addToPortfolio(R.head(res.data)))
.then((portfolio) => dispatch(add(portfolio)));
}
// action creator
export function add(portfolio) {
return {
type: ADD_COIN,
portfolio
}
}
The reducer
import { ADD_COIN } from './actions'
const initialState = [];
export default (state = initialState, action) => {
switch(action.type) {
case ADD_COIN:
return action.portfolio;
default:
return state;
}
}
the reducer/index.js
import { combineReducers } from 'redux'
import portfolio from './portfolio'
export default combineReducers({
portfolio
});
Apart from azium answer, you can use actions like this. It saves you some writing,
export default connect(null, {addCoin})(SearchCoin)
and you can use it like this,
clickCoin(coin) {
console.log('clickCoin', coin);
this.props.addCoin(coin);
this.props.closeSearch();
}
The problem is that you are wrapping your function with an extra function.
Change:
const mapDispatchToProps = (dispatch) => ({
selectCoin(coin) {
return () => { <--- returning extra function
dispatch(addCoin(coin))
}
}
})
to:
const mapDispatchToProps = (dispatch) => ({
selectCoin(coin) { dispatch(addCoin(coin)) }
})