Can I use Redux for the real time data - reactjs

I am new to ReactJS and exploring it for an application that requires real time data.
Lets say I have "n" components that have to show the real time data (1 second update). The app should consolidate all the "n" components' related "tags" and call the webserver which gives the real time data for all the "tags"( I cannot make a call from each component as it will be a huge load. So I have to consolidate and make a single call).
There is a component "RealTimeDataFetcher" which has a timer and invokes an action "GETLIVEDATA" every second. This action calls the webservice and returns the response (Using thunk). DataReducer returns the updated state and all the "n" components uses the MapStateToProps for getting the live data.
Actions
export const subscribeForData = tag =>{
return {type : 'SUBSCRIBE' , payload : tag}
}
export const getLiveData = tag[] =>{
const response = await makeServiceCall(tag[]);
dispatch({ type: 'LIVE_DATA', payload: response.data });
}
Reducer
export const liveData = (livedata, action){
// update state for live data
}
export const subscribedTags= (tags, action){
// update state for subscribed tags
}
RealTimeDataFetcher
class RealTimeDataFetcher extends React.Component{
componentDidMount(){
// call subscribeData for fetching livedata for "tags"
}
}
function mapStateToProps (state){
return { subscribedTags: state.subscribedTags};
}
connect(mapStateToProps,{...})(RealTimeDataFetcher)
Component
class SomeComponent extends React.Component{
ComponentDidMount(){
this.props.subscribeForData("tag");
}
ComponentDidUpdate(){
// I will get the live data here.
}
}
function mapStateToProps (state){
return { liveData: state.liveData};
}
connect(mapStateToProps,{...})(SomeComponent )
This approach is working. But I am not sure if this is the right way to get the real time data.
Is there any other approach that I can look into.

Related

Accessing JSON Array Data in React.js/Next.js Combo

Given an API which returns JSON data like:
["posts":
{"id":1,
"name":"example",
"date":"exampledate",
"content":"examplecontent",
"author":"exampleauthor"},
{"id":2,
..]
The length of the array is unknown.
I am fetching data via isomorphic-fetch like this:
displayPosts.getInitialProps = async function() {
const res = await fetch('.../post');
const data = await res.json();
return{
posts: data.posts
}
}
which is working (console.log.stringify(data)).
Now i want to display such posts on my displayPosts page.
Therefore i am using the following React Component.
class Posts extends React.Component {
stat = {
// here i don't know how to set the state
}
render() {
return (
// i want to render the data here
);
}
}
export default Posts;
Question: How do i set a state, so that i can neatly display every post in my displayPosts.js page with
<Posts posts={props.Posts}/>
?
class Posts extends React.Component {
state = {
posts: []
}
componentDidMount() {
this.savePosts();
}
componentDidUpdate() {
this.savePosts();
}
savePosts = () => {
if(this.props.posts){
//do any other processing here first if you like
this.setState({
posts: this.props.posts
});
}
}
You probably don't need to save the posts in state, since you could just pull them from props directly. But if you need to process or transform them somehow, it might make sense.
In either case, you just hook into the lifecycle methods to do this.
Note: This will set the state every time the component updates. Often you only want to update the state when that specific prop changes. If so, you can first compare the old and new props to see if it has changed in a way that means you want to update your state.

What is the correct way to access data fetched from a redux thunk function?

I am currently using this pattern, but it feels a bit wrong. I don't think a redux thunk should return a string, and the component should only access the data fetched through the connected props. I may be wrong, but is there a better, more idiomatic way of doing this?
I want to keep the two thunks separate so that I can call each one individually.
// actions
const putCustomer = ...;
const putBooking = ...;
// thunks
export async function fetchCustomer(customerId) {
return dispatch => {
const customer = await customerApi.fetch(customerId);
dispatch(actions.putCustomer( {customer} ));
};
}
export async function fetchBooking(bookingId) {
return dispatch => {
const booking = await bookingApi.fetch(bookingId);
dispatch(actions.putBooking( {booking} ));
// I AM RETURNING THE BOOKING DATA HERE
return booking;
};
}
// component
class MyComponent extends React.Component {
async componentWillMount() {
const booking = await this.props.dispatch(fetchBooking(this.props.bookingId));
// I WANT TO ACCESS THE BOOKING DATA HERE
this.props.dispatch(fetchCustomer(booking.customerId));
}
}
QUESTION UPDATED
Added some comments where I want to access the data
In Ideal case below is the how the flow goes incase you dispatch some action :
It is suggested that UI can be bind to state and state is been modified by reducer , its a single direction flow. The data you are trying to return can be managed via state and the dispatcher can modify that state

How to save fetched data from server to component state using redux and redux-thunk?

In my react app I have component named profile, and I am fetching data from server and showing it inside that component. I am using redux and redux-thunk along with axios. With help of mapDispatchToProps function, i am calling redux action for fetching that data when component is mounted and saving it to redux state. After that, using mapStateToProps function i am showing that data on the screen via props. That works fine. Now I want to have possibility to edit, for example, first name of that user. To accomplish that i need to save that data to component state when data is fetched from server, and then when text field is changed, component state also needs to be changed. Don't know how to save data to component sate, immediately after it is fetched.
Simplified code:
const mapStateToProps = (state) => {
return {
user: state.user
}
}
const mapDispatchToProps = (dispatch) => {
return {
getUserData: () => dispatch(userActions.getUserData())
}
}
class Profile extends Component {
state:{
user: {}
}
componentDidMount (){
this.props.getUserData()
// when data is saved to redux state i need to save it to component state
}
editTextField = () => {
this.setState({
[e.target.id]: e.target.value
})
};
render(){
const { user } = this.props;
return(
<TextField id="firstName"
value={user.firstName}
onChange={this.editTextField}
/>
)
}
}
You can use componentDidUpdate for that or give a callback function to your action.
I will show both.
First lets see componentDidUpdate,
Here you can compare your previous data and your present data, and if there is some change, you can set your state, for example if you data is an array.
state = {
data: []
}
then inside your componentDidUpdate
componentDidUpdate(prevProps, prevState) {
if(prevProps.data.length !== this.props.data.length) {
// update your state, in your case you just need userData, so you
// can compare something like name or something else, but still
// for better equality check, you can use lodash, it will also check for objects,
this.setState({ data: this.props.data});
}
}
_.isEqual(a, b); // returns false if different
This was one solution, another solution is to pass a call back funtion to your action,
lets say you call this.props.getData()
you can do something like this
this.props.getData((data) => {
this.setState({ data });
})
here you pass your data from redux action to your state.
your redux action would be something like this.
export const getData = (done) => async dispatch => {
const data = await getSomeData(); // or api call
// when you dispatch your action, also call your done
done(data);
}
If you are using React 16.0+, you can use the static method getDerivedStateFromProps. You can read about it react docs.
Using your example:
class Profile extends Component {
// other methods here ...
static getDerivedStateFromProps(props) {
return {
user: props.user
}
}
// other methods here...
}

React Redux Dispatch

What is the best way to call a dispatch to get initial data on a React component. My understanding is that ComponentWillMount is called before render. So in theory if I call dispatch on ComponentWillMount, by the time I hit render and then ComponentDidMount I should have my data in the component's props, right? I'm not seeing that.
I'm seeing that render gets called twice and that on the first go when the component is being initialized, I cannot access the data in props. It also seems like dispatch does not actually get called until the second render. I'm basically looking to have some light shed on the best way to call a dispatch when initially setting up a component. I'm essentially trying to do something like the following where I use a container component to get my data from dispatch and then pass it to a child component as props. But I also want to initialize some state variables in the ContainerComponent and then pass them to the ChildComponent as props. The thing is that the state variables I want to initialize depend on the data returned from dispatch and ideally I would do the initialization in ComponentWillMount or ComponentDidMount.
import React from 'react';
import axios from 'axios';
import { connect } from 'react-redux';
import ChildComponent from './ChildComponent.js';
import { getTransactionsAll } from '../actions/actions.js';
class ContainerComponent extends React.Component {
constructor() {
super();
this.state = {
acctList:[],
acctChecked:[],
categoryList:[]
}
}
componentWillMount() {
console.log("componentWillMount entered");
this.props.get_data();
console.log(this.props.searchProps.transactions_all);//this is undefined meaning the dispatch has not assigned the data yet...??
}
componentDidMount() {
console.log("componentDidMount entered");
console.log(this.props.searchProps.transactions_all);//this is undefined meaning the dispatch has not assigned the data yet...??
}
render() {
console.log("TransactionManagerContainer render entered");
console.log(this.props.searchProps.transactions_all);//this is undefined the first time around meaning the dispatch has not assigned the data yet...??, but is defined on the second call to render after the dispatch has actually occurred...
return <ChildComponent
data={this.props.searchProps.data}/>;
}
const mapStateToProps = (state) => ({
searchProps: state.searchProps
});
export default connect(mapStateToProps, {getTransactionsAll})(TransactionManagerContainer);
Here is my reducer that assigns the state:
import { combineReducers } from 'redux'
import {GET_TRANSACTIONS } from '../actions/actions.js'
import {GET_TRANSACTIONS_ALL } from '../actions/actions.js'
const INITIAL_STATE = { defaultYear: 2016, transactions: []};
function get_transactions(state = INITIAL_STATE, action) {
// console.log("this is in the reducer: get_transactions");
// console.log(action);
switch(action.type) {
case GET_TRANSACTIONS:
// return { ...state, transactions: action.payload };
return Object.assign({}, state, {
transactions: action.payload,
selectedYear: action.selectedYear
})
default:
return state;
}
}
function get_transactions_all(state = INITIAL_STATE, action) {
console.log("this is the value of action in the reducer: get_transactions_all");
console.log(action);
switch(action.type) {
case GET_TRANSACTIONS_ALL:
// return { ...state, transactions: action.payload };
return Object.assign({}, state, {
transactions_all: action.payload
})
console.log("this is the value of state in the reducer after being set");
console.log(state);
default:
return state;
}
}
const rootReducer = combineReducers({
//stateProps: get_transactions,
searchProps: get_transactions_all
})
export default rootReducer
Here are my actions:
import axios from 'axios';
export const GET_TRANSACTIONS = 'GET_TRANSACTIONS';
export function getTransactions(year) {
return function(dispatch) {
axios.get(`http://localhost:3001/api/transfilter?year=${year}&grouping=2`)
.then(response => {
dispatch({
type: GET_TRANSACTIONS,
payload: response.data,
selectedYear: year
});
})
.catch((error) => {
console.log(error);
})
}
}
export const GET_TRANSACTIONS_ALL = 'GET_TRANSACTIONS_ALL';
export function getTransactionsAll(year) {
return function(dispatch) {
axios.get(`http://localhost:3001/api/trans?limit=20`)
.then(response => {
dispatch({
type: GET_TRANSACTIONS_ALL,
payload: response.data
});
})
.catch((error) => {
console.log(error);
})
}
}
I believe your main question is:
What is the best way to call a dispatch to get initial data on a React component?
Getting initial data requests (or any AJAX requests in general) should go in the componentDidMount lifecycle event.
There are a few reasons for this, here are two important:
Fiber, the next implementation of React’s reconciliation algorithm, will have the ability to start and stop rendering as needed for performance benefits. One of the trade-offs of this is that componentWillMount, the other lifecycle event where it might make sense to make an AJAX request, will be “non-deterministic”. What this means is that React may start calling componentWillMount at various times whenever it feels like it needs to. This would obviously be a bad formula for AJAX requests.
You can’t guarantee the AJAX request won’t resolve before the component mounts. If it did, that would mean that you’d be trying to setState on an unmounted component, which not only won’t work, but React will yell at you for. Doing AJAX in componentDidMount will guarantee that there’s a component to update.
Credits: I learned that from here, there is also a discussion here.
Then, there are a lot of smaller question you've raised and it will be hard for me to answer all, but I'll try to cover most:
After reading the above, you now should understand why your data is undefined in componentWillMount and componentDidMount. That's simply because the data has not arrived yet;
It's normal that your data is undefined during the first render of the component. Initial render happens before data arrival;
It's normal that the data is defined during the second render. The dispatch triggers asynchronous data fetch. Right after data comes, a reducer is hit and component gets re-rendered (that's the second re-render).
If the child components in your main component require the data - check in the parent render method if data exists pass internal components conditionally, only if data is present. Like so:
class ContainerComponent extends React.Component {
// ... omitted for brevity
render() {
return (
{ this.props.searchProps.data ?
<ChildComponent
data={this.props.searchProps.data} />
: <p>Loading</p>
}
);
}
}

Best practice for making small changes in UI with React and Redux

I'm using Redux and React to load data from a web service which is working well. I'd like to make small non-webservice-based changes in the UI in response to an action. A simplified example:
class SmartComponent extends React.Component {
handleClick = (e) => {
// how to best handle a simple state change here?
}
render() {
const { displayMessage } = this.props
return (
<DumbComponent message={displayMessage}/>
<button onclick={this.handleClick}>Change Message</button>)
}
}
const mapStateToProps = state => {
// state variables linked in the reducer
}
export default connect(mapStateToProps)(SmartComponent)
let DumbComponent = ({ message }) => {
return ({message})
}
If I modify the state in SmartComponent, for instance, by using this.setState, the props of SmartComponent will not be automatically updated. I believe it's a React anti-pattern to directly modify the props of SmartComponent. Is the best way to update the message in DumbComponent to make an action creator and link it in the reducer? That seems a bit overkill for a simple message change.
Yes, you should link it to the reducer.
However this is not mandatory:
How to do it
One other way to do this would be to store the message in the state of the SmartComponent.
Beware about the fact that Redux is no longer the single source of truth for the message.
class SmartComponent extends React.Component {
constructor(props) {
super(props)
// Initialize state based on props
this.state = {
message: props.message,
}
}
componentWillReceiveProps(nextProps) {
// Handle state update on props (ie. store) update
this.setState({ message: ... })
}
handleClick = (e) => {
this.setState({ message: ... })
}
render() {
const { displayMessage } = this.state
return (
<DumbComponent message={displayMessage}/>
<button onclick={this.handleClick}>Change Message</button>)
}
}
const mapStateToProps = state => {
// state variables linked in the reducer
}
export default connect(mapStateToProps)(SmartComponent)
let DumbComponent = ({ message }) => {
return ({message})
}
Should you do it ?
If the data you display in this component can be completely isolated from the rest of your application, that is to say no dispatched action could modify it, and no other component need it, keeping this data in the store can be unnecessary.
I mostly use this method to perform optimistic updates to the view component without altering the store until new value is saved by the server.

Resources