Props return undefined ( React ) - reactjs

I want to access my props.group in my ViewGroup component, but my props return undefined
I tried with a constructor but it's producing the same undefined.
enter image description here
ViewGroup.js
class ViewGroup extends React.Component{
render() {
console.log(this.props.group, ' CONSOLE LOG PROPS GROUP')
return(
<div style={styles.base}>
</div>
)
}
}
DataGroup Component
import ViewGroup from './ViewGroup'
class DataGroup extends React.Component{
render(){
const { dataGroups } = this.props
return(
<div>
<ViewGroup
group={dataGroups}
/>
</div>
)
}
}
const mapStateToProps = state => {
const dataGroups = state.groupReducer.group ? state.groupReducer.group :[]
return {
dataGroups
}
};
export default connect(mapStateToProps)(DataGroup)
Actions.js
export function loadGroup(data){ return { type: LOAD_GROUP, data }};
export default function groupReducer( state= {}, action = {}){
switch (action.type){
case LOAD_GROUP:
return {
...state,
group: action.data
}
default:
return state
}
}
Reducer
const reducer = combineReducers({
Auth,
groupReducer,
persistStore,
form: formReducer
});

I think this: const dataGroups = state.groupReducer.group ? state.groupReducer.group :[] should actually be: const dataGroups = state.group ? state.group :[].

Related

state and props are undefined

Hi Im fairly new to coding. I am using react redux and created a store, reducers etc. I cant get anything to render to the screen and when I console.log this.props it comes up an empty array. The data Im dealing with is an array of objects I set the initialstate to an array of objects. I connected all the components with connect and mapstateto props function, there is an empty object in the first arguement of the connect and the second arguement is the componenet. When I look at my component tree it looks fine but my state is undefined and cant figure out why? Here is my code. Thank you in advance.
export const FETCH_SMURFS_START ="FETCH__SMURFS_START"
export const FETCH_SMURFS_SUCCESS = "FETCH_SMURFS_SUCCESS"
export const FETCH_SMURFS_FAILURE ="FETCH_SMURFS_FAILURE"
export const ADD_SMURF_START = "ADD_SMURF_START"
export const ADD_SMURF_SUCCESS = "ADD_SMURF_SUCCESS"
export const ADD_SMURF_FAILURE = "ADD_SMURF_FAILURE"
export const getSmurfData = () =>dispatch=>{
dispatch({type:FETCH_SMURFS_START})
console.log(FETCH_SMURFS_START)
axios.get(' http://localhost:3333/smurfs')
.then((res)=>{
console.log(res.data)
dispatch({type:FETCH_SMURFS_SUCCESS, payload:res.datay})
})
.catch((err)=> {
dispatch({type:FETCH_SMURFS_FAILURE, payload:err.message})
})
}
export const putSmurfData = () =>dispatch=>{
dispatch({type:ADD_SMURF_START})
console.log(ADD_SMURF_START)
dispatch({ADD_SMURF_SUCCESS})
axios.put(' http://localhost:3333/smurfs')
.then((res)=>{
dispatch({type:ADD_SMURF_SUCCESS, payload:res.data})
})
.catch((err)=> {
dispatch({type:ADD_SMURF_FAILURE, payload:err.message})
})
}
class SmurfDisplay extends React.Component {
componentDidMount() {
getSmurfData();
}
render() {
return (
<>
<div>
{this.props.newSmurfData.map((smurf, index) => (
<div>
<h4 key={smurf.id}></h4>
<p> {index}</p>
<p>{smurf.description}</p>
<p>{smurf.nickname}</p>
<p>{smurf.name}</p>
<p>{smurf.position}</p>
</div>
))}
</div>
</>
);
}
}
const mapStateToProps = (state) => {
return {
newSmurfData: [
{
error:state.error,
id: state.id,
name: state.name,
position: state.position,
nickname: state.nickname,
description: state.description,
},
],
};
};
export default connect(mapStateToProps, {})(SmurfDisplay);
class Smurf extends React.Component {
render() {
console.log(this.props);
return (
<>
{this.props.smurfData.map(function(smurf,index) {
return(
<div>
<h4 key={index}></h4>
<p>{smurf.description}</p>
<p>{smurf.nickname}</p>
<p>{smurf.name}</p>
<p>{smurf.position}</p>
</div>
)
})}
</>
)
}
}
const mapStateToProps = (state) =>{
return{
smurfData:[{
error:state.error,
id:state.id,
name:state.name,
position:state.position,
nickname:state.nickname,
description:state.description
}]
}
}
export default connect(mapStateToProps,{})(Smurf)
xport const initialState = {
error:"",
isLoading: false,
smurfData : [{
id:"",
name:"",
position:"",
nickname:"",
description:""
}],
error:"",
isAddingSmurf:false,
newSmurfData:[{
id:"",
name:"",
position:"",
nickname:"",
description:""
}],
}
export const reducer = (state = initialState,action) =>{
switch(action.type){
case FETCH_SMURFS_START:
return{
...state,
isLoading:true,
error:""
}
case FETCH_SMURFS_SUCCESS:
return{
...state,
isLoading:false,
smurfData:action.payload
}
case FETCH_SMURFS_FAILURE:
return{
...state,
isLoading:false,
error:"there was an error getting your smurfs"
}
case ADD_SMURF_START:
return{
...state,
isAddingSmurf:false,
error:""
}
case ADD_SMURF_SUCCESS:
return{
...state,
isAddingSmurf:true,
error: "",
newSmurfData:action.payload
}
case ADD_SMURF_FAILURE:
return{
...state,
isAddingSmurf:false,
addingError:"Error"
}
default:
return state
}
}
export default reducer;
when you want to use this.props in your class Component you need to use super(props) inside the constructor:
constructor(props){
super(props)
}

Need help to call an action using redux as well as update Component State on a single event of Click?

I am trying to learn Redux by simply add/delete users. I have an action 'ADD_PROFILE', with payload : name,account-number. On clicking add button, I wanted to update the store, hide the 'add user' form and show a message 'User added successfully'. If it is in React, I can have a boolean state variable, update/reset variable and switch the views. If I wanted to do the same using Redux bit not sure how.
This is what I tried :
Action
export const addProfile = (name, account_number) => {
console.log(name, account_number)
return{
type :'ADD_PROFILE',
payload : {
name : name,
account_number : account_number
}
};
}
Reducer:
export default (profilesList=[],action) => {
switch(action.type){
case 'ADD_PROFILE':
return [...profilesList, action.payload]
case 'DELETE_PROFILE':
return profilesList.filter(name => name!== action.payload.name)
default:
return profilesList;
}
}
AddView.js
import React from 'react';
import { connect } from 'react-redux';
import { addProfile } from '../actions';
class AddView extends React.Component{
constructor(props) {
super(props);
this.state={
isProfileAdded: false
};
}
addValuesView(){
return(
<div>
Name : <input type="text" value={this.props.profiles.name} ref={el => (this.nameInputRef = el)}/>
Account Number : <input type="text" value={this.props.profiles.account_number} ref={el => (this.accountInputRef = el)}/>
<button onClick={() => {
this.setState(isProfileAdded=true),
this.props.addProfile(this.nameInputRef.value,this.accountInputRef.value)
}
}>Add</button>
</div>
);
}
profileAddedView(){
return(
<div>Profile added succesfully</div>
)
}
view(){
return !this.props.profiles.isProfileAdded ? this.addValuesView() : this.profileAddedView()
}
render(){
console.log(this.state)
return this.view()
}
}
const mapStateToProps = (state) => {
return { profiles : state.profiles }
}
const mapDispatchToProps = dispatch => ({
onAddProfile: dispatch(addProfile())
});
export default connect(mapStateToProps, {addProfile}) (AddView);
App.js
import React from 'react';
import AddView from './AddView';
const App = () =>{
return (
<div className="ui container">
<AddView />
</div>
);
}
export default App;
Method this.setState should receive an object:
() => {
this.setState({ isProfileAdded: true});
this.props.addProfile(this.nameInputRef.value, this.accountInputRef.value);
}

Updating Item Listing using React, Redux. and Redux Form

How do I update the horse listing after the add horse action is fully done?
I think that the reloadHorseList in CreateHorse is running before createHorse actions is completely done so sometimes I see new horse in list and sometimes not. A full reload shows an update always.
Horses Component
...
import { getHorses } from '../../actions';
import ListHorses from './ListHorses';
import CreateHorse from './forms/createHorseForm';
class Horses extends React.Component {
constructor(props) {
super(props);
this.state = {
...
};
this.reloadHorseList = this.reloadHorseList.bind(this);
}
componentDidMount() {
this.reloadHorseList();
}
reloadHorseList() {
this.props.getHorses(this.props.current_user.selected_stable);
}
render() {
return (
<div className="content page-content-wrapper1">
<CreateHorse
current_user={this.props.current_user}
reloadHorseList={this.reloadHorseList}
/>
<ListHorses
current_user={this.props.current_user}
horses={this.props.horses}
/>
</div>
);
}
}
function mapStateToProps(state) {
return {
horses: state.horses
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
getHorses: getHorses
},
dispatch
);
}
export default connect(mapStateToProps, mapDispatchToProps)(Horses);
Create Horse Form
...
import { Field, reduxForm, getFormValues } from 'redux-form';
import {
createHorse,
getHorseSelect,
updateHorseCount
} from '../../../actions';
import { connect } from 'react-redux';
const renderField = (...
);
class CreateHorse extends Component {
constructor(props) {
super(props);
this.state = {
...
};
this.setMessage = this.setMessage.bind(this);
}
onSubmit(props) {
//let p = this.props.reloadHorseList;
try {
this.props.createHorse(props, this.setMessage);
//running before I have finished creating my horse
this.props.reloadHorseList();
} catch (err) {
...
}
}
render() {
const { handleSubmit } = this.props;
return (
<div>
...
{this.state.displayHorseCreateForm && (
<div>
<h4 className="header-content">Add Horse</h4>
<p> * required field</p>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
// fields here
<button type="submit" className="btn btn-primary">
Submit
</button>
</form>
</div>
)}
</div>
);
}
}
function validate(values) {
...
}
function mapStateToProps(state) {
---
}
export default connect(mapStateToProps, {
createHorse,
getHorseSelect,
updateHorseCount
})(
reduxForm({
form: 'HorseCreatetForm',
initialValues: {
...
},
validate
})(CreateHorse)
);
//create horse action
export const createHorse = (props, setMessage) => async dispatch => {
try {
const request = await axios.post(`/api/horse/create`, props);
return {
type: CREATED_HORSE,
payload: request.data
};
} catch (err) {
...
}
};
ListHorses
...
import { deleteHorse } from '../../actions';
class HorsesList extends React.Component {
render() {
let horses = this.props.horses;
let horseCount = this.props.horse_count;
return (
<div className="content">
horse count: {horseCount}
<ul className="list-inline box-body">
{horseCount > 0 &&
horses.map((horse, key) => (
<li key={key}>
...//listing here
</li>
))}
</ul>
</div>
);
}
}
function mapStateToProps(state) {
return {
horse_count: state.horse_count
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
...
},
dispatch
);
}
export default connect(mapStateToProps, mapDispatchToProps)(HorsesList);
The solution that worked for me is to send a callback to the CreateHorse component to send to the createHorse action which runs Horse components action to getHorses.
class Horses extends React.Component {
constructor(props) {
super(props);
this.state = {
horses: this.props.horses,
};
this.reloadHorses = this.reloadHorses.bind(this);
}
componentDidMount(prevProps) {
this.props.getHorses(this.props.current_user.selected_stable);
}
reloadHorses = () => {
this.props.getHorses(this.props.current_user.selected_stable);
};
...
<CreateHorse
current_user={this.props.current_user}
reloadHorses={this.reloadHorses}
/>
<ListHorses
horses={this.props.horses}
/>
...
function mapStateToProps(state) {
return {
horses: state.horses
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
getHorses: getHorses
},
dispatch
);
}
export default connect(mapStateToProps, mapDispatchToProps)(Horses);
then in CreateHorse component
onSubmit(props) {
this.props.createHorse(props, this.setMessage, this.props.reloadHorses);
}
}
Then in the createHorse action
export const createHorse = (
props,
setMessage,
reloadHorses
) => async dispatch => {
try {
const request = await axios.post(`/api/horse/create`, props);
reloadHorses();
return {
type: CREATED_HORSE,
payload: request.data
};
} catch (err) {
...
}
};
You should be posting real code at this point. To trigger a component re render you need to be changing it's state. I would recommend setting your props from redux into local state and render your list from that. You also will need to be using componentWillRecieveProps();
componentDidMount() {
this.reloadHorseList();
this.setState=({list: this.props.horseList});
}
componentWillRecieveProps(nextProps){
this.setState=({list: nextProps.horseList})
}
You are correct in your assumption that the component finishes loading first. So you need to utilize the componentWillRecieveProps lifecycle hook .
Alternatively, if you're using mapStateToProps() with redux your component should be rerendering when anything within mapStateToProps() changes.

Using different state in a nested object in two same component - reactjs

I would like to ask if how to dispatch or catch the data in mapStateToProps if data that I want to get is in a nested state and the identifier would be the this.props.group that is passed in FilmList via the Parent Component.
// Parent Component
<Row>
<FilmList group="upcoming" groupTitle="Upcoming Movies" />
<FilmList group="top_rated" groupTitle="Top Rated Movies" />
</Row>
// Child Component
class FilmList extends React.Component {
constructor(props){
super(props);
}
componentDidMount(){
this.props.getMovieByGroup(this.props.group);
}
renderFilmItem(){
if(this.props.data){
var film = this.props.data.upcoming.slice(0,6).map((item) => {
return <FilmItem key={item.id} film={item} />
});
return film;
}
}
render(){
console.log('new');
return(
<div className={styles.filmContainer}>
<h1>{ this.props.groupTitle }</h1>
{ this.renderFilmItem() }
</div>
);
}
}
function mapStateToProps(state){
return {
data: state.film.data.upcoming
}
}
This is what my state looks like:
This is my reducer:
const INITIAL_STATE = {
data: {},
error: {},
};
function processData(initialData, data) {
let updated = initialData;
updated[data.group] = data.results;
return updated;
}
export default (state = INITIAL_STATE, action) => {
switch(action.type) {
case GET_FILM_SUCCESS: {
return Object.assign({}, state.data[action.data.group], {
data: processData(state.data,action.data)
});
}
case GET_FILM_FAILURE: {
return { ...state, error: action.data }
}
}
return state;
}
Currently in my mapStateToProps I only access state.film.data.upcoming what I want to achieve is like state.film.data.{this.props.group} somewhere along that code so it will re render the component when "top_rated" or "upcoming" data state change.
So if state.file.data.upcoming is working fine, then you should be able to use state.file.data in mapStateToProps then do state.file.data[this.props.group] in your component.

React component not re-rendering on state change with connect

I am creating a todo app where TodoApp is the main Component which contain filterLink a simple link and a VisibleTodoList section, whenever I toggle the todolist it changes its state and re render the filter component which is not using connect but VisibleTodoList(which is using connect) is not re render
class TodoApp extends Component {
render(){
return (
<div className="App">
<input ref={node =>{
this.input=node;
}}/>
<button onClick={
()=>{
store.dispatch(actions.addTodo(count++,this.input.value));
}
}>Add Todo</button>
<FilterLink filter='SHOW_ALL' store={store.getState()}/>
<FilterLink filter='ACTIVE' store={store.getState()}/>
<FilterLink filter='COMPLETED' store={store.getState()}/>
<VisibleTodoList/>
</div>
);
}
}
VisibleTodoList contain
const getVisibleTodos= (visible,todo)=>{
switch(visible){
case 'ACTIVE':
return todo.filter((item)=>item.completed === false)
case 'COMPLETED':
return todo.filter((item) => item.completed === true);
default :
return todo;
}
}
const mapStateToProps=(state)=>({
todo: getVisibleTodos(state.visiblity,state.todo)
})
const mapDispatchToProps=(dispatch)=>{
return {
onTodoClick:(id)=>{
dispatch(actions.toggleTodo(id));
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoItem)
TodoItem contain
class TodoItem extends Component {
render(){
return(
<ul>
{this.props.todo.map((item)=>
<TodoList key={item.id} {...item} onTodoClick={this.props.onTodoClick}
/>
)}
</ul>
);
}
}
TodoList contain
class TodoList extends Component {
render(){
console.log(this.props)
const {id,text,completed,onTodoClick} =this.props;
return (
<li
style={{textDecoration:completed?'line-through':''}}
onClick={()=>onTodoClick(id)}>{text}</li>
)
}
}
The combine reducer contain todo and visiblity reducer
function visible(state='SHOW_ALL',action){
switch(action.type){
case 'VISIBLITY_FILTER':return action.filter;
default:return state;
}
}
index.js(reducer)
function todo(state=[],action){
switch(action.type){
case 'ADD_TODO':
return[
...state,{
id:action.id,
text:action.text,
completed:false
}
];
case 'TOGGLE_TODO':
let t= state.map(item =>{
if(item.id === action.id){
item.completed =!item.completed;
}
});
return state;
default: {
return state;
}
}
}
const todos=combineReducers({
todo,
visiblity
})
This is the corresponding action creator which is imported as actions
export const toggleTodo=(id)=>{
return{
type:'TOGGLE_TODO',
id:id
}
}
export const addTodo=(count,val)=>{
return {
id:count,
text:val,
type:'ADD_TODO'
}
}

Resources