In my top-level component App.js, I have
componentWillMount() {
let data = [];
for (let i = 0; i < 10; i++) {
let todayURL = 'http://www.someURL.com/';
let viewDate = moment()
.add(1, 'years')
.add(i, 'days')
.format('MMM-DD');
let date = moment()
.add(1, 'years')
.add(i, 'days')
.format('YYYY-MMM-DD');
let cardURL =
todayURL +
moment()
.add(1, 'years')
.add(i, 'days')
.format('YYYY/MMM-DD.png');
data.push({
id: i,
date,
uri: cardURL
});
}
store.dispatch({ type: 'data_generated', payload: data });
My reducer is handled this way:
export default (state = [], action) => {
case 'data_generated':
return { ...state, calendarData: action.payload };
default:
return state;
}
};
I combine my reducers:
import { combineReducers } from 'redux';
import CalendarReducer from './CalendarReducer';
export default combineReducers({
calendarData: CalendarReducer
});
The idea behind this is to have an initial dataset that the app can use and persist.
In a child component that displays the information in the dataset I access state:
render() {
console.log(this.props)
return( blah blah);
}
const mapStateToProps = state => {
return { calendarData: state.calendarData };
};
export default connect(mapStateToProps)(HomeScreen);
But when I console log I see an empty array:
This is a React Native app using React Navigation.
What's going on?
After a lengthy chat I've figured out what was wrong. Unfortunately for many of us, React, just like native JavaScript, is very good at letting you place your code improperly.
You were configuring your route within a component and had plenty of logic in the life cycle functions, one of which was dispatching data to the store which was never reaching your reducers.
Your problem can be solved, as proven in our chat, by separating your action code inside your App.js to a dedicated action.js file and then importing and adding the same function to mapDispatchToProps inside your Home.jsx. And finally, on componentWillMount() of Home.jsx you invoke the action by this.props.actionName()
Your code does not seem to follow any conventions recommended by Reacts developers themselves.
If you want to improve your React's programming skills along with your programming skills in general, I recommend you the following Udemy course by Stephen Grider.
Try call the props in the constructor on the component. It's a good debut to debug
Hope it helps
constructor(props) {
super(props);
console.log(this.props);
this.state = {
calendarData: props.calendarData
};
}
componentWillReceiveProps(nextProps) {
if (this.props.calendarData !== nextProps.calendarData) {
this.setState({
calendarData: nextProps.calendarData,
});
}
}
Related
What does work:
Saga pulls the data from an API. The reducer for UPDATE_LOTS fires up and returns the new state.
Redux store is updated with the correct data as can be observed in the chrome extension and through logging.
What doesn't work:
The componentDidUpdate never fires up. Nor does componentWillReceiveProps when replaced by it.
Since the component never received an update, there's no re-rendering either.
Most of the advice on this topic discusses how people accidentally mutate the state, however in my case I don't do that. I've also tried the following construction {...state, lots: action.data} instead of using ImmutableJS's Map.set with no luck.
Any ideas? Not listing the saga files here because that part of the data flow works perfectly.
The component:
class Lots extends React.Component {
constructor(props) {
super(props);
this.props.onFetchLots();
}
componentDidUpdate() {
console.log('updated', this.props.lots);
}
render() {
const lots = this.props.lots;
console.log('render', lots);
return (lots && lots.length) > 0 ? <Tabs data={lots} /> : <div />;
}
}
Mapping and composition:
function mapDispatchToProps(dispatch) {
return {
onFetchLots: () => {
dispatch(fetchLots());
},
};
}
function mapStateToProps(state) {
return {
lots: state.lots,
};
}
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
const withReducer = injectReducer({ key: 'lots', reducer: lotsReducer });
const withSaga = injectSaga({ key: 'lots', saga });
export default compose(
withReducer,
withSaga,
withConnect,
)(Lots);
Reducer:
export const initialState = fromJS({
lots: false,
});
function lotsReducer(state = initialState, action) {
switch (action.type) {
case FETCH_LOTS:
console.log('fetch lots');
return state;
case UPDATE_LOTS:
console.log('update lots', action.data.data);
return state.set('lots', action.data.data);
default:
return state;
}
}
Everything was correct except for the mapStateToProps function.
Since ImmutableJS was used, I had to access the state property as state.get("lots") instead of state.lots.
Doing so fixed the problem.
I've always used react-redux connect to configure props but I need to use a react Component to use lifecycle methods. I'm noticing that my props that I'm grabbing from the store seem to be static and they do not update as the store updates.
Code:
class AlertModal extends Component {
title
isOpen
message
componentDidMount() {
const { store } = this.context
const state = store.getState()
console.log('state', state)
console.log('store', store)
this.unsubscribe = store.subscribe(() => this.forceUpdate())
this.title = state.get('alertModal').get('alertModalTitle')
this.isOpen = state.get('alertModal').get('isAlertModalOpen')
this.message = state.get('alertModal').get('alertModalMessage')
this.forceUpdate()
}
componentWillUnmount() {
this.unsubscribe()
}
updateAlertModalMessage(message) {
this.context.store.dispatch(updateAlertModalMessage(message))
}
updateAlertModalTitle(title) {
this.context.store.dispatch(updateAlertModalTitle(title))
}
updateAlertModalIsOpen(isOpen) {
this.context.store.dispatch(updateAlertModalIsOpen(isOpen))
}
render() {
console.log('AlertModal rendered')
console.log('AlertModal open', this.isOpen) <======= stays true when in store it is false
return (
<View
How do I set up title, isOpen, and message so they reflect the store values at all times?
It should be something like this. In your Confirmation component:
const mapStateToProps = (state) => {
return { modalActive: state.confirmation.modalActive };
}
export default connect(mapStateToProps)(Confirmation);
In your reducer index file, is should be something like this:
const rootReducer = combineReducers({
confirmation: ConfirmationReducer
});
I believe you have your own reducer file called ConfirmationReducer here. It should be something like this.
import { ON_CONFIRM } from '../actions';
const INITIAL_STATE = {modalActive: true};
export default function(state = INITIAL_STATE, action) {
console.log(action);
switch (action.type) {
case ON_CONFIRM:
return { ...state, modalActive: action.payload };
}
return state;
}
Make sure you write your own action creator to create an action with the above type and relevant payload of boolean type.
Finally you should be able to access the property from the store inside your Confirmation component like this:
{this.props.modalActive}
You have not posted entire code, so it makes very difficult to give a solution to the exact scenario. Hope this helps. Happy coding!
For me the problem was that I was assigning this.props.myObject to a variable which wasn't deep cloned so I fixed it by using
let prev = Object.assign({}, this.props.searchData)
What I was doing
let prev = this.props.searchData
So I was disturbing the whole page.Seems quiet noob on my part.
this may help you
componentWillReceiveProps(nextProps) {
console.log();
this.setState({searchData : nextProps.searchData})
}
I've been working on a React app and have gotten to a point where I'll need Redux to handle some aspects of it.
After reading a bunch of tutorials, I'm fairly stuck on how to make my "smarter" components "dumber" and move functions into my actions and reducers.
So, for example, one aspect of the app is more of a to-do list style.
One of my classes starts like this:
export default class ItemList extends React.Component {
constructor() {
super();
this.state = { items: [],
completed: [],
};
this.addItem = this.addItem.bind(this);
this.completeItem = this.completeItem.bind(this);
this.deleteItem = this.deleteItem.bind(this);
}
addItem(e) {
var i = this.state.items;
i.push({
text: this._inputElement.value,
paused: false,
key: Date.now()
});
this.setState({ items: i });
e.preventDefault();
this._inputElement.value = '';
this._inputElement.focus();
}
completeItem(e) {
this.deleteItem(e);
var c = this.state.completed;
c.push({
text: e.target.parentNode.parentNode.getElementsByClassName('item-name')[0].innerHTML,
paused: false,
key: Date.now()
});
this.setState({ completed: c });
}
deleteItem(e) {
var i = this.state.items;
var result = i.filter(function(obj) {
return obj.text !== e.target.parentNode.parentNode.getElementsByClassName('item-name')[0].innerHTML;
});
this.setState({ items: result });
}
// ... more irrelevant code here ...
// there's a function called createTasks that renders individual items
render() {
var listItems = this.state.items.map(this.createTasks);
return <div className="item-list">
<form className="form" onSubmit={this.addItem}>
<input ref={(a) => this._inputElement = a}
placeholder="Add new item"
autoFocus />
<button type="submit"></button>
</form>
{listItems}
</div>;
}
}
So, as you can see, it's very logic-heavy. I've started adding Redux by adding a <Provider> in my index file, and made a basic reducers file that is fairly empty so far:
import { combineReducers } from 'redux';
const itemList = (state = {}, action) => {
};
// ... other irrelevant reducers
const rootReducer = combineReducers({
itemList,
// ...
});
export default rootReducer;
...and I've made an actions file that doesn't have much in it yet either.
I've been struggling to figure out:
Most actions I've seen examples of just return some kind of JSON, what do I return in the reducer that uses that JSON that my component can use?
How much of my component logic is reusable, or should I just forget it? What is the best way to go about this to reuse as much code as I've written as possible?
First of all you need to understand the overall picture of how redux works with react.
Before coming to that lets first understand what are smart components and dumb components.
Smart Components
All your code logic needs to be handled here
They are also called containers.
They interact with the store(aka state management) to update your components.
Dumb Components
They just read props from your containers and render you components
This is just the UI view and should not contain any logic.
All styling/html/css comes in your dumb components.
Here is an amazing piece of article which you can go through to understand smart and dumb components if you still have doubts.
Ok, now lets try understanding how redux works:-
Your smart components(aka containers) interact with your redux store
You fire actions from your containers.
Your actions call your apis
The result of your action updates the store through a reducer
You containers read the store through mapStateToProps function and as soon as value in store changes it updates your component.
Now lets consider your todo example
TodoListContainer.js
class TodoListContainer extends Component {
componentWillMount () {
// fire you action action
}
render () {
return (
<Todos todos=={this.props.todos} />
)
}
}
function mapStateToProps(state) {
const {todos} = state;
return {
todos;
}
}
export default connect(mapStateToProps)(TodoListContainer)
TodoList.js
class TodoList extends Component {
renderTodos() {
return this.props.todos.map((todo)=>{
return <Todo todo={todo} key={todo.id} />
})
}
render () {
return () {
if (this.props.todos.length === 0) {
return <div>No todos</div>
}
return (
<div>
{this.renderTodos()}
</div>
)
}
}
}
export default class TodoList
Todo.js
class Todo extends Component {
render () {
return (
<div>
<span>{this.props.todo.id}</span>
<span>{this.props.todo.name}</span>
</div>
)
}
}
Reducer
export default function todos(state={},action) {
switch (action.type) {
case 'RECEIVE_TODOS':
return Object.assign(state,action.todos);
}
}
action
function fetchTodos() {
return(dispatch) => {
axios.get({
//api details
})
.then((res)=>{
dispatch(receiveTodos(res.todos))
})
.catch((err)=>{
console.warn(err)
})
}
}
function receiveTodos(todos) {
return {
type: 'RECEIVE_TODOS',
todos
}
}
Now if you have read redux documentation you would see that actions return objects then how would i call my api there which returns a function instead of an object. For that I used redux thunk about which you can read here.
I gave you an example in which you can fetch todos. If you want to do other operations like deleteTodo, addTodo, modifyTodo then you can do that in appropriate components.
DeleteTodo - you can do in TodoListContainer.
AddingTodo - you can do in TodoListContainer.
Changing State(completed/Pending) - you can do in TodoListContainer.
ModifyingTodo - you can do in TodoContainer.
You can also check out here for a detailed example, but before that I would say just should go through basics of redux which you can find here
P.S: I wrote code on the fly so it might not work properly but it should work with little modification.
I am having server render on a demo react application of mine. Although it works if i refresh the page on a url to fetch the doctor like /doctor/:id if i am at /login and try to go to /doctor/123456 the doctor property is empty and (this.props.doctor.name.first) fails.
What is a good approach to use redux to fetch data on these cases?
Code is below
import { fetchDoctor } from '../../DoctorActions';
import { getDoctor } from '../../DoctorReducer';
class DoctorDetailPage extends Component {
render() {
return (
<div>{this.props.doctor.name.first}</div>
);
}
}
DoctorDetailPage.need = [params => {
return this.props.dispatch(fetchDoctor(params.id));
}];
function mapStateToProps(state, props) {
return {
doctor: getDoctor(state, props.params.id),
};
}
DoctorDetailPage.propTypes = {
doctor: PropTypes.shape({
insurance: PropTypes.string,
description: PropTypes.string,
GUID: PropTypes.string,
name: PropTypes.shape({
first: PropTypes.string,
last: PropTypes.string,
})
}),
dispatch: PropTypes.func.isRequired,
};
export default connect(mapStateToProps)(DoctorDetailPage);
REDUCER
import { ADD_DOCTOR } from './DoctorActions';
// Initial State
const initialState = { list: [] };
const DoctorReducer = (state = initialState, action = {}) => {
switch (action.type) {
case ADD_DOCTOR:
return {
list: [action.doctor, ...state.list],
};
default:
return state;
}
};
export const getDoctor = (state, id) => {
return state.doctors.list.filter(doctor => doctor._id === id)[0];
};
export default DoctorReducer;
ACTIONS
import callApi from '../../util/apiCaller';
// Export Constants
export const ADD_DOCTOR = 'ADD_DOCTOR';
// Export Actions
export function addDoctor(doctor) {
return {
type: ADD_DOCTOR,
doctor,
};
}
export function addDoctorRequest() {
return () => {
return true;
};
}
export function fetchDoctor(id) {
return (dispatch) => {
return callApi(`doctors/${id}`)
.then(res => dispatch(addDoctor(res)));
};
}
LOG ERROR
TypeError: Cannot read property 'name' of undefined
What is a good approach to fetch data in general?
A user friendly approach would be to enter the page /doctor/123456 wihtout the need of having the doctor available, so user get's instant feedback that his action (navigate me to page x) worked. In onEnter method of react-router or in componentDidMount you should start an action fetchDoctor and in a meanwhile show the user a spinner or a message indicating that the data is being loaded.
render() {
return (
<div>
{ this.props.doctor && <div>{this.props.doctor.name.first}</div> }
{ ! this.props.doctor && <YourSpinnerComponent/> }
</div>
);
}
So the above render method shows something while data is being loaded and when data comes in it displays it without any errors.
What is a good approach to fetch data using redux?
The "good old" way to handle async operations is to use redux-thunk. You can read this great SO answer about dispatching asynchronous redux actions.
The latest trend is to use redux-saga. It is a library that aims to make side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) in React/Redux applications easier and better. More about redux-saga.
So in your case, you would create a Saga to handle the fetching.
More about redux-thunk vs redux-saga in this great SO answer.
I am creating a React application and integrating Redux to it in order to manage the state and do network requests.
I followed the Todo tutorial and I am following the async example from the redux website, but I am stucked.
Here is my problem, I want, in my application, to fetch a user from a remote server. So the server send me a json array containing an object (maybe it's better if the server send me directly an object ? )
The json I obtain looks like that (I put only two fields but there are more in real) :
[{first_name: "Rick", "last_name": "Grimes"}]
Anyway I can fetch the data from the server but I can't inject user's data into my application, I hope you can help me but the most important is that I understand why it doesn't work.
Here are my several files :
I have two actions, one for the request and the other for the response:
actions/index.js
export const REQUEST_CONNECTED_USER = 'REQUEST_CONNECTED_USER';
export const RECEIVE_CONNECTED_USER = 'RECEIVE_CONNECTED_USER';
function requestConnectedUser(){
return {
type: REQUEST_CONNECTED_USER
}
}
function receiveConnectedUser(user){
return {
type: RECEIVE_CONNECTED_USER,
user:user,
receivedAt: Date.now()
}
}
export function fetchConnectedUser(){
return function(dispatch) {
dispatch(requestConnectedUser());
return fetch(`http://local.particeep.com:9000/fake-user`)
.then(response =>
response.json()
)
.then(json =>
dispatch(receiveConnectedUser(json))
)
}
}
reducer/index.js
import { REQUEST_CONNECTED_USER, RECEIVE_CONNECTED_USER } from '../actions
function connectedUser(state= {
}, action){
switch (action.type){
case REQUEST_CONNECTED_USER:
return Object.assign({}, state, {
isFetching: true
});
case RECEIVE_CONNECTED_USER:
return Object.assign({}, state, {
user: action.user,
isFetching: false
});
default:
return state;
}
}
And I have finally my container element, that I have called Profile.js
import React from 'react';
import { fetchConnectedUser } from '../actions';
import { connect } from 'react-redux';
class Profile extends React.Component{
constructor(props){
super(props);
}
componentDidMount() {
const { dispatch } = this.props;
dispatch(fetchConnectedUser());
}
render(){
const { user, isFetching} = this.props;
console.log("Props log :", this.props);
return (
<DashboardContent>
{isFetching &&
<div>
Is fetching
</div>
}
{!isFetching &&
<div>
Call component here and pass user data as props
</div>
}
</DashboardContent>
)
}
}
function mapStateToProps(state) {
const {isFetching, user: connectedUser } = connectedUser || { isFetching: true, user: []}
return {
isFetching,
user: state.connectedUser
}
}
export default connect(mapStateToProps)(Profile)
In the code above, I always have the Is Fetching paragraph being display, but even when I remove it, I cannot access to user data.
I think I have the beginning of something because when I console.log I can see my actions being dispatched and the data being added to the state, but I think I am missing something in the link communication with this data to the UI Component.
Am I on the good way or do I have a lot of things to refactor ? Any help would be very helpful !
Seeing as you are immediately fetching the data I allow myself to assume that isFetching may be true at beginning. Add an initial state to your reducer
state = { isFetching: true, user: null }
Then assuming you setup the reducer correctly:
function mapStateToProps(state) {
const {isFetching, user } = state.connectedUser
return {
isFetching,
user
}
}
Hope this works, feels clean-ish.