I have been creating a todo app with Django backend and react Frontend using REACT API. The action is successfully dispatched and data fetched successfully, but in the RenderTodo Component the data has not appeared. Below are the attached files.
Definitely, there's just small thing I'm missing but can't get it!
I'm somewhat new to this thing so, need help.
Is it anything like declaring an empty state in TodoComponent.js?
I have also attached the rendered form in the browser at last.
Along with below data I have now put it on GitHub here.
Directory Structure is
Files:
configureStore.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import logger from 'redux-logger';
import { Todos } from './reducers/todo';
export const ConfigureStore =() => {
const store = createStore(
combineReducers({
todos: Todos,
}),
composeWithDevTools(applyMiddleware(thunk, logger))
);
return store;
}
ActionCreators.js
import * as ActionTypes from './ActionTypes';
import { baseUrl } from './baseUrl';
import axios from 'axios';
//GET_TODOS
export const getTodos = () => (dispatch) => {
axios
.get('api/todo/')
.then(res => {
dispatch({
type: ActionTypes.GET_TODOS,
payload: res.data
});
}).catch(err => console.log(err));
}
todo.js
import * as ActionTypes from '../ActionTypes';
export const initialState = {
todos: []
}
export const Todos = (state=initialState, action) => {
switch(action.type) {
case ActionTypes.GET_TODOS:
return {
...state,
todos: action.payload
};
default:
return state;
}
};
App.js
import React, { Component, Fragment } from "react";
//used for providing store to all child components
import { Provider } from "react-redux";
import Todo from "./components/TodoComponent";
import { ConfigureStore } from "./redux/configureStore";
const store = ConfigureStore();
export class App extends Component {
render() {
return (
<Provider store={store}>
<Fragment>
<Todo />
</Fragment>
</Provider>
);
}
}
export default App;
TodoComponent.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { getTodos } from "../redux/ActionCreators";
import RenderTodo from "./RenderTodoComponent";
const mapStateToProps = (state) => ({
todos: state.todos.todos,
});
const mapDispatchToProps = (dispatch) => ({
getTodos: () => {
dispatch(getTodos());
},
});
class Todo extends Component {
componentDidMount() {
this.props.getTodos();
}
render() {
return (
<div className="Main">
<h1>Todos</h1>
<RenderTodo todos={this.props.todos} />
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Todo);
RenderComponent.js
import React from "react";
export default function RenderTodo(props) {
return (
<div>
{console.log("second component")}
<table className="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Text</th>
<th>Completed</th>
<th>Created_at</th>
<th />
</tr>
</thead>
<tbody>
{props.todos.map((todo) => {
<tr key={todo.id}>
<td>{todo.id}</td>
<td>{todo.text}</td>
<td>{todo.created_at}</td>
<td>{todo.completed}</td>
<td>
<button className="btn btn-danger btn-sm">Delete</button>
</td>
</tr>;
})}
</tbody>
</table>
</div>
);
}
browser
console printing second component is a debugging statement showing the rendering of the second component and then the first components' componentDidMount is called.
EDIT: Added redux-dev tools -images
I have attached action, state and diff, hope this helps in troubleshooting. Everything seems fine here
You aren't RETURNing anything from props.todos.map
Replace the { and } with ( and ) to return the JSX.
{props.todos.map((todo) => ( // this was previously a {
<tr key={todo.id}>
<td>{todo.id}</td>
<td>{todo.text}</td>
<td>{todo.created_at}</td>
<td>{todo.completed}</td>
<td>
<button className="btn btn-danger btn-sm">Delete</button>
</td>
</tr>
))}
Related
I am developing a lottery statistics app that gets data from a csv loaded from an input then I was wanting to read this data to the redux store so I can use it across multiple components.
I have successfully saved the data to the redux store once I import the file and read it through Header.js and using an action, but I am not sure how to access this in other components like e.g. Main.js.
I feel like I am still confused on how react/redux all fits together. I'm sorry if this has been asked before but everything I looked up online I couldn't get to work.
// index.js
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import reducers from "./reducers";
import App from "./components/App";
const store = createStore(reducers, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector("#root")
);
// App.js
import React from "react";
import Header from "./Header";
import Main from "./Main";
const App = () => {
return (
<div>
<Header />
<Main />
<div className="numbers-for-draw"></div>
</div>
);
};
export default App;
// Header.js
import React from "react";
import { CSVReader } from "react-papaparse";
import { fetchData } from "../actions";
import { connect } from "react-redux";
class Header extends React.Component {
constructor(props) {
super(props);
this.fileInput = React.createRef();
}
handleReadCSV = data => {
this.props.fetchData(data);
console.log(this.props.data);
};
handleOnError = (err, file, inputElem, reason) => {
console.log(err);
};
handleImportOffer = () => {
this.fileInput.current.click();
console.log("Got to handleImportOffer");
};
render() {
return (
<header>
<CSVReader
onFileLoaded={this.handleReadCSV}
inputRef={this.fileInput}
style={{ display: "none" }}
onError={this.handleOnError}
/>
<button onClick={this.handleImportOffer}>Import</button>
</header>
);
}
}
//Map what is in the redux store (e.g. state) to props
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps, {
fetchData: fetchData
})(Header);
// Main.js
import React from "react";
import { fetchData } from "../actions";
import { connect } from "react-redux";
const Main = () => {
console.log("In main");
console.log(this.props.data); //Blows up here.
return <div>Main</div>;
};
//Map what is in the redux store (e.g. state) to props
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps, {
fetchData: fetchData
})(Main);
// actions/index.js
export const fetchData = data => dispatch => {
console.log("Action");
const lottoData = {
stringNumbers: [
"one",
"two",
"three",
...
],
allResults: [],
winningNumbers: [],
winningNumbersAsStrings: []
};
const localData = data.data;
localData.shift();
localData.forEach(line => {
const lineObject = {
draw: line[0],
drawDate: line[1],
ballOne: line[2],
ballTwo: line[3],
ballThree: line[4],
ballFour: line[5],
ballFive: line[6],
ballSix: line[7],
bonusBall: line[8],
bonusBall2: line[9],
powerBall: line[10]
};
lottoData.allResults.push(lineObject);
let nums = [];
nums.push(parseInt(line[2]));
nums.push(parseInt(line[3]));
nums.push(parseInt(line[4]));
nums.push(parseInt(line[5]));
nums.push(parseInt(line[6]));
nums.push(parseInt(line[7]));
nums.sort((a, b) => {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
lottoData.winningNumbers.push(nums);
lottoData.winningNumbersAsStrings.push(nums.toString());
});
dispatch({ type: "FETCH_DATA", payload: lottoData });
};
// lottoReducer.js
export default (state = {}, action) => {
switch (action.type) {
case "FETCH_DATA":
return action.payload;
default:
return state;
}
};
// reducers/index.js
import { combineReducers } from "redux";
import lottoReducer from "./lottoReducer";
export default combineReducers({
data: lottoReducer
});
I haven't tested your code, but it seems to me that the only problem is in your Main.js
While you use a function component and not a class, you shouldn't use this to access your props. The following should work as expected:
const Main = (props) => {
console.log("In main");
console.log(props.data);
return <div>Main</div>;
};
//Map what is in the redux store (e.g. state) to props
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps, {
fetchData: fetchData
})(Main);
In your main.js you used functional components so this.props doesn't work there. You must pass props to your component and console.log(props.data).
I have tried a lot but i couldn't figure out what is the issue.
The props in the component is coming as empty even after adding mapStateToProps and mapDispatchToProps property.Whenever i run the below code i get following error.
projList.js:94 Uncaught TypeError: _this2.props.addNewProj is not a function
My component class is given below:
import React from 'react';
import { addProj } from '../actions';
import { connect } from 'react-redux';
import C from '../constants';
class projList extends React.Component {
constructor(props){
super(props);
this.state = {
title: ''
}
}
render(){
const {title} = this.state;
return(
<section className='proj-list-container'>
<div className='form'>
<label>project Title</label>
<input type='text' onChange={(e)=>{this.setState({title: e.target.value})}}/>
<button className='submit' onClick={()=>{this.props.addNewProj(title)}}>submit</button>
</div>}
</section>
);
}
}
const mapStateToProps = (state, props) =>
({
projLists: state.addProjToList
})
const mapDispatchToProps = dispatch =>
({
addNewProj(projObj) {
dispatch(
addProj(C.ADD_PROJ, projObj)
);
}
});
export default connect (mapStateToProps, mapDispatchToProps)(projList);
export default projList;
My actions file is
import C from './constants'
export const addProj = ({title, endDate}) => {
return ({
type:C.ADD_PROJ,
payload: {
title, endDate
}
})
}
And my store file is :
import C from '../constants';
import { combineReducers } from 'redux';
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
export const addProjToList = (state=[], action) => {
switch (action.type) {
case C.ADD_PROJ :
return [
...state,
action.payload
]
default : return state
}
}
const appReducer = combineReducers({
addProjToList
});
export default (initialState={projList: []}) => {
return applyMiddleware(thunk)(createStore)(appReducer, initialState);
}
any help would be greatly appreciated. Thanks!
I have a search bar on my TODO List:
import React, { Component } from 'react';
import { FilterTasks } from '../../redux/actions/searchbar';
import reducers from '../../redux/reducers';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
class SearchBar extends Component {
render() {
const {search, value} = this.props;
return (
<input
className="form-control"
placeholder = "Filter Tasks"
onChange={(e) => FilterTasks(e.target.value)}
value={value} />
);
}
}
function mapStateToProps({tasks}) {
return {value: tasks.value};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({FilterTasks}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(SearchBar);
Here's my action to filter:
export const SEARCH = 'SEARCH';
export function FilterTasks(value) {
return {type: SEARCH, value};
}
My search bar reducer:
import {SEARCH} from '../actions/searchbar';
const initialState = {}
export default function SEARCHREDUCER(state = initialState, action) {
switch(action.type) {
case SEARCH: {
const {value} = action;
const tasks = state.contents.filter((val) => val.includes(value));
return {...state, value, tasks};
}
default:
return state;
}
}
My Index reducer:
import { combineReducers } from 'redux';
import SEARCHREDUCER from '../reducers/searchbar';
const TaskReducer = (state = [] ,action) => {
switch (action.type){
case 'ADD_TASK':
state = state.concat(action.payload);
break;
case 'DELETE_TASK':
state = state.tasks.filter(task => task !== action.payload)
break;
}
return state;
},
reducers = combineReducers({
tasks : TaskReducer,
SEARCHREDUCER
});
export default reducers;
And my TasksList class where the filtered list should be rendered:
import React, { Component } from 'react';
import {connect} from 'react-redux';
import Task from '../task'
class TaskList extends Component {
render(){
return(
<table>
<thead>
<tr>
<th>Tasks</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{this.props.tasks.map((task,index) => <Task key={index} task={task} />)}
</tbody>
</table>
);
}
}
function MapStateToProps(state){
return{
tasks:state.tasks,
}
}
export default connect (MapStateToProps)(TaskList);
My problem here is that when I type an entry on the search bar the Tasks list does not change at all, It's not showing any kind of error. What i'm missing here?
FilterTasks(e.target.value) should be this.props.FilterTasks(e.target.value) instead, otherwise it'll call the the imported function from actions that is not bound to Redux by your mapDispatchToProps.
Also, your TaskReducer and SEARCHREDUCER are wrong. The reducer variable is the one with the combined state, not TaskReducer or SEARCHREDUCER.
You should just keep the search string in state and do the filtering within TaskList with this.props.tasks.filter(<insert filter function>).map(<insert map function>).
I am utilizing thunk with react and redux. My action creator executes an API call and returns data in a property "data" and my reducer returns that object. I have this returned object mapped to props in my component. Its an array of 16 items (each item is an image url). when I console.log(this) I can click through and see the data, but if I go further like console.log(this.props.showGallery.imageLinks) it shows undefined.
Another situation is that when I render { this.props.showGallery.imageLinks } I can clearly see all the text of the items in the array on my web page but when I use .map on it, the console says cannot read property "map" of undefined and the web page is just empty. Am I doing this wrong? How can I make this data like normally?
Am I understanding redux concepts wrongly?
actions.js
export const SHOW_GALLERY = 'SHOW_GALLERY';
import axios from 'axios';
// FETCH THE IMAGES
export const actionGallery = () => {
return ( dispatch ) => {
axios.get('../images')
.then(res => {
if (res.status === 200) {
return dispatch({ type: SHOW_GALLERY, data: [...res.data] });
}
})
.catch(err => console.error(err));
}
}
reducer
images.js
import { HEAD_SELECTED, SHOW_GALLERY } from '../actions/actions';
import { actionSelectHeadImg } from '../actions/actions';
import { actionGallery } from '../actions/actions';
// Show the image links from an array
export function showGallery(state={}, action) {
switch(action.type) {
case SHOW_GALLERY:
return Object.assign({}, state, { imageLinks: new Array(action.data) })
default:
return state;
}
}
combined Reducers for above:
import React from 'react';
import { combineReducers } from 'redux';
import { showGallery, headSelected } from './images';
// Combine all your reducers to this
const allReducers = combineReducers({
showGallery,
headSelected
});
export default allReducers;
component / container
Gallery.js
import React from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actionGallery } from '../actions/actions';
import cheerio from 'cheerio';
class Gallery extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
}
this.props.actionGallery();
}
render() {
return (
<div>
<h1>This is the Gallery.</h1>
<br />
<div className="container">
<div className="row">
<div className="col-md-8">
<h2>H2 h2 h2 2h2 </h2>
{ this.props.showGallery.imageLinks.forEach((i, index) => {
<p>i</p>
}) }
</div>
</div>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
showGallery: state.showGallery,
headSelected: state.headSelected,
newState: state
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
actionGallery,
actionSelectHeadImg
}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Gallery);
A regular component that just holds the other container/components
import React from 'react';
import ReactDOM from 'react-dom';
import Gallery from './containers/Gallery';
import HeadingImg from './containers/HeadingImg';
class App extends React.Component {
render() {
//console.log(this.props.actionGallery())
return (
<div>
<center>
<h3>SELECTED IMAGE:</h3>
<br />
<HeadingImg />
<hr />
<Gallery />
</center>
</div>
);
}
}
export default App;
TOP LEVEL COMPONENT (MAIN COMPONENT)
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import App from '../Components/Earth/app';
import allReducers from '../Components/Earth/reducers';
import thunk from 'redux-thunk';
const store = createStore(allReducers, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<App store={store}/>
</Provider>
, document.getElementById("root"));
I'm assuming when you create your store, you only have the one reducer. If that is the case then your assumption about 'state.showGallery' existing doesn't. Instead imageLinks will be in state without the 'showGallery'.
If my assumption is correct, then you should change your mapStateToProps to have showGallery as:
showGallery: { imageLinks: state.imageLinks },
Hi All I am new to redux. I am creating a sample app as below:
entry point: index.js
import 'babel-polyfill'
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import inboundApp from './reducers'
import App from './components/App'
let store = createStore(inboundApp)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
)
/components/App.js
import React from 'react'
import HeaderContainer from '../containers/HeaderContainer'
import LoginForm from '../containers/LoginForm'
const App = () => (
<div>
<HeaderContainer />
<LoginForm />
</div>
)
export default App
/containers/LoginForm.js
import React from 'react'
import { connect } from 'react-redux'
import { login } from '../actions'
let LoginForm = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(login(input.value))
input.value = ''
}}>
<input ref={node => {
input = node
}} />
<button type="submit">
Login
</button>
</form>
</div>
)
}
LoginForm = connect()(LoginForm)
export default LoginForm
/actions/index.js
export const login = (supplierId) => {
return {
type: 'LOGIN',
supplierId
}
}
/containers/HeaderContainer.js
import { connect } from 'react-redux'
import Header from '../components/Header'
const mapStateToProps = (state) => {
return {
supplierId: state.supplierId
}
}
const HeaderContainer = connect(
mapStateToProps,
null
)(Header)
export default HeaderContainer
/components/Header.js
import React, { PropTypes } from 'react'
const Header = ({ supplierId}) => {
return (
<div>
<span>Your Supplier ID: </span> {supplierId}
</div>
)
}
export default Header
/reducers/loginForm.js
const loginForm = (state = '', action) => {
switch (action.type) {
case 'LOGIN':
return Object.assign({}, state, {
supplierId: action.supplierId
})
default:
return state;
}
}
export default loginForm
/reducers/index.js
import { combineReducers } from 'redux'
import loginForm from './loginForm'
const inboundApp = combineReducers({
loginForm
})
export default inboundApp
The problem is my presentation component Header does not get update by the action LOGIN which is firing by click on the button in the LoginForm.js.
would you please help me to find what am I missing? what's wrong with this code?
thanks
I think you try to get the supplierId, from the wrong namespace and your default state of loginForm is not good. Try like that:
const loginForm = (state = {supplierId: ''}, action) => {
switch (action.type) {
case 'LOGIN':
return Object.assign({}, state, {
supplierId: action.supplierId
})
default:
return state;
}
}
And the connect
const mapStateToProps = (state) => {
return {
supplierId: state.loginForm.supplierId
}
}