React stateless functional component with dynamic Redux state and SignalR - javascript

I would appreciate feedback on my approach:
BACKGROUND
The application needs to have ReactJS components that will fetch data from server.
The components will receive frequent updates from the server.
There will be more than one component using the same data model.
There will be different types of data model.
It has to be easy to add new components just changing the data-model name.
The application will probably be configurable.
APPROACH
Core:
I created a class called DataFetch that will fetch data from the server, which optionally can receive the Redux state options, SignalR options and callbacks using the methods onReceive() and onError().
When not configurated with Redux state options and SignalR options: the dataFetch will have the same axios functionality.
When configurated with Redux state options: the DataFetch class will dynamically create a redux state according the state options and keep it updated using the result from request. The Redux state options are: stateName and reducer. The actionTypes will be dynamically created using the stateName and for on success/error response of the request.
When configurated with Signal options: the dataFetch receive real-time updates from server. if configurated: It will update the redux state and will call the callbacks (onSuccess, onError).
Adicional information:
I'm using axios to fetch data from server.
This state has been dynamically created using the official redux approach: ReducerManager.
I'm using Core SignalR to add the real-time functionality to the application.
I created a hook useSignalR to easily use SignalR with React components.
All this is separated into their respective classes, functions to easily be tested.
How to use?
I created a hook called useDataFetch to easily use this functionality with `stateless React components.
The result from the hook will be a object like this: { data, error, isLoading }.
The syntax to use the hook is like this:
import React from 'react';
import { useSignalRContext } from './core/signalR';
import {
useDataFetch,
SignalRHubOptions,
ReduxStoreOptions
} from './core/dataFetch';
const ExampleComponent = props => {
const { dataModelId } = props;
const { someHubConnection } = useSignalRContext();
const signalRHubOptions = useMemo(
() =>
new SignalRHubOptions({
hubConnection: someHubConnection,
// Multiple components can be created for the same hubConnection.
// Therefore, we need to add a dataChangeMethodName by the component instance,
// otherwise, all instances of that component will receive the same message.
dataChangeMethodName: `DataChanged-${dataModelId}`
}),
[someHubConnection, dataModelId]
);
const reduxStoreOptions = useMemo(
() =>
new ReduxStoreOptions({
// Use diferrent stateName for different conponents.
stateName: `${dataModelId}_state`,
initialState: []
}),
[dataModelId]
);
// Receive data when:
// - perform the request with success;
// - signalR send a message;
// - redux state receive a update with the actionType SUCCESS;
// Receive error when:
// - perform the request with error;
// - redux state receive a update with the actionType FAILURE.
const { data, error, isLoading } = useDataFetch({
url: '/endpoint/${dataModelId}',
data: {
param1: 'value1'
},
signalRHubOptions: signalRHubOptions,
reduxStoreOptions: reduxStoreOptions
});
return {
/* render the component with the data or error or isLoading state */
};
};
export default ExampleComponent;

Related

Redux state in regualar javascript function

I am working on a ReactJS application, which uses many API calls to get data from the server. These API calls are organized into multiple classes and all of them use another class which issues a HTTP request using Axios.
Example markup and flow: (this is hand typed to keep it simple, please ignore any minor mistake or omissions)
class MyComponent extends Component{
componentDidMount(){
this.props.getData(//parameters//); //getData is an action bound via react-redux connect.
}
}
export const getData = (//parameters//) => (
dispatch: any
) => {
return new Promise((resolve, reject) => {
//Some business logic
let axiosService = new axiosService();
axiosService .get(//parameters//).then((data: any) => {
dispatch({
type: "RECEIVE_DATA",
payload: data
});
resolve();
});
});
};
export default class AxiosService {
config: AxiosRequestConfig;
url : string;
constructor() {
this.url = "http://localhost:8080/api/";
this.config = {
headers: {
//I want to inject Authorization token here
},
responseType: "json"
};
}
get = () => {
return new Promise((resolve, reject) => {
resolve(axios.get(this.url, this.config));
});
};
}
Now I want to inject auth token as Authorization header when AxiosService makes an API call. This token is available in redux store. How can I access redux store in my service?
I can pass the token as a parameter to AxiosService get call, but that is messy. Based on my reading custom thunk middleware might help me here, but not sure how to update my service call. I have many actions, that need to use AxiosService.
There are many ways. For e.g. you can call Axios from the reducer or from the action builder. The reducer have the state, and redux-thunk is a popular library for that, that gives you the store to the Action Builder.
You can also use redux-saga, to listen to actions and perform http requests in response. redux-saga also gives you the store.
If you don't want all the libraries you can do a workaround. Where you created the store, save it to the global window:
window.store=redux.createStore(...)
Then, your http library can access the store:
window.store.getState()
It's not recommended way, but it's working.

Apollo-client local state cache race condition?

So I have been trying to use apollo-boost in a React app to use the cache to manage my client state using #client directives on queries but I have been having some issues.
Basically I'm using writeQuery() to write a boolean to my local app state in a Component (let's call it component A) and want to get that value in another Component (let's call it component B) using readQuery() inside the componentDidUpdate method. The thing is, readQuery() in Component B is running before writeQuery in Component A sets the value in the cache/local state so the value read by Component B comes out wrong.
I've confirmed this by using setTimeout to delay the readQuery() and indeed after using the timeout, the value is correct, but this solution can't be trusted, I'm probably not aware of something in Apollo Client because this functionality is pretty basic for local state management. Any Tips?
I believe that in Redux this is solved because the state is being injected to props, which makes the component update, so being that Component A is the one that changes the state, component B wouldn't even have to use componentDidUpdate to get the new value, since the state would be injected and Component B would get updated with the correct value.
Any help would be appreciated, sorry if I didn't make myself clear!
EDIT: The writeQuery() is being used inside a Mutation resolver.
Methods like readQuery and writeQuery are meant to be used to read and modify the cache inside of mutations. In general, they should not be used inside of components directly. By calling readQuery, you are only fetching the data from the cache once. Instead, you should utilize a Query component.
const TODO_QUERY = gql`
query GetTodos {
todos #client {
id
text
completed
}
}
`
<Query query={TODO_QUERY}>
{({ data }) => {
if (data.todos) return <ToDoListComponent todos={data.todos}/>
return null
}}
</Query>
The Query component subscribes to relevant changes to the cache, so the value of data will update when your cache does.
Similarly, you should create appropriate mutations for whatever changes to the cache you're going to make, and then utilize a Mutation component to actually mutate the cache.
const client = new ApolloClient({
clientState: {
defaults: {
todos: []
},
resolvers: {
Mutation: {
addTodo: (_, { text }, { cache }) => {
const previous = cache.readQuery({ query: TODO_QUERY })
const newTodo = { id: nextTodoId++, text, completed: false, __typename: 'TodoItem' }
const data = {
todos: previous.todos.concat([newTodo]),
}
cache.writeQuery({ query, data })
return newTodo
},
},
}
}
})
<Mutation mutation={ADD_TODO}>
{(addTodo) => (
// use addTodo to mutate the cache asynchronously
)}
</Mutation>
Please review the docs for more details.

How to pass an new props from a parent component with React and Redux

This is my component's class:
import React, { Component } from 'react';
import {connect} from 'react-redux';
import Button from '../UI/Button/Button';
import * as actions from '../../store/actions';
class Password extends Component {
submitPassword=(e)=>{
e.preventDefault();
this.props.submitPassword(this.state.password, this.props.levelNumber);
}
render() {
<Button clicked={this.submitPassword} >
Submit password
</Button>
}
}
const mapStateToProps = state => {
return {
};
}
const mapDispatchToProps = dispatch => {
return {
submitPassword: (password,levelNumber) => dispatch(actions.submitPassword(password,levelNumber))
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Password);
and this is my action:
export const submitPassword = () => {
// HERE ALL MY LOGIC !!!
return {
level:undefined,
type:actions.PASSWORD
}
}
The code working all correctly including params and logic.
I wanna that every time that finish to execute the function submitPassword A third component refresh/reload with the new props. Attention! My third component is parent, not child!
It's possibile to send a command from action to component? How can I do it? I have already tried with:
componentWillReceiveProps() {
console.log("new props");
}
in my component but he can not take the event.
normally a structure my redux store as follows
{
entities: {},
ui: {},
domain:{}
}
so for example when you execute submitPassword you execute all the logic and depending on the result you can dispatch another action to update the ui or the domain part so the componentes that are connected respond to those changes.
The UI holds information about UI changes, like if you are submiting info display a progress bar or if it has to display a dialog.
The domain part holds information related to the whole webapp, like loggin info and notifications.
You don't need always to pass new props for redux state to be accessed.
As redux is having immutable state, you'll always be getting new updated state no matter the previous one. So this will enforce your component to update props to get latest state from redux. This is done by <Provider> wrapper attached on root level.
So hence your props will be having new values whenever redux state gets updated.
The lifecycle you are looking is static getderivedstatefromprops(). This lifecycle gets executed whenever props are changed/updated.
I made visual implementation on fly that can aid you the picture. Here Redux State means Redux Store

How to rework an Angular1 App to a VueJs app

So I'll get right to it :
I'm trying to remake an existing app that used Angular 1 , instead using VueJS 2.
Being unfamiliar to Angular1 I find it challenging to decide on a few things :
1.What are factories(probably services), where do I place/write manage them?
2.Okay I know angular is big on controllers, but I cannot seem to understand if I were to use Vue , what's the alternative to the controller, and where to hold the code.
So, basically what I've gotten so far is to use VueX for state management and I have moved some services there, however - I can't figure out for example if a certain service #requires 'NameOfOtherService' , does it mean it imports it like in NodeJS const NameOfOtherService = require ('insert/path/here'); ?
Basically the app gets data from an API and php scripts, for example :
In the angular 1 version in my appsettings, which is using an AppConfig module I have a pathFromScript(script) => {} // etc.
My question is , how do I manage EVERYTHING that's going on within one app like that translated to Vue?
Thank you in advance I know it's entirely a shot in the dark here.
Yours truly ,
As for Vue.Js, your angular controllers are methods. If you need to get some data from an API, you can use a method to call a Vuex action and either return the value back to the method or add it to your state, so it's available everywhere. This will look like the example below.
I guess your factories / services are managing API calls. I would place that inside Vuex with axios to make API calls.
Methods is the same as controllers in Angular. You create methods on the specific page. If you need a method to be resused multiple places, you can use mixins. Mixins extends the specific page by importing it. You can read about it here: https://v2.vuejs.org/v2/guide/mixins.html
page.vue
{{data}}
export default {
import { mapActions, mapGetters } from 'vuex'
...
computed: {
...mapGetters({
data: 'getData'
})
},
methods: {
...mapActions({
getServerData: 'getDataFromServer'
})
},
created () {
this.getServerData()
}
}
store.js (vuex)
import axios from 'axios'
state: {
data: null
},
getters: {
getData: state => {
return state.data
}
},
mutations: {
setDataFromServer: (state, payload) => {
state.data = payload
}
},
actions: {
getDataFromServer: ({ commit }) => {
return axios.get('https://website.com/api/data').then((response) => {
commit('setDataFromServer', response)
})
}
}
In this example the created is calling the vuex action to get the data from the server. When the server returns the value, it calls a mutation which sets the response in a state (data). The mapGetters which calls the getters in vuex, returns the data from state. It updates whenever the state changes.
Hope this makes sense

Is Flux the right solution to get async data to my React.JS views

I am currently designing the architecture of a webapp using React.JS. In React, data flow is unidirectional. Sometimes (often) we want to communicate between views. React solves this problem with flux. So far so good, I now have stores that holds the (shared) data.
Now I wonder if flux is also the right solution for this old problem:
A view, say a table needs data from a server that it should render. I am not that experienced with React therefor pardon me if it's a stupid question, but is flux also the right solution for getting data from a server and parse it to the registered views? If so, is there a best practice to work with async data? If not, what would be an appropriate way to fetch data from the server for a react view?
I actually don't want a view to call for the data. In my opinion a view should be as stupid as possible...
..is flux also the right solution for getting data from a server and
parse it to the registered views?
You can do this with flux. Flux is al about Action Creators. In the flux architecture the idea is that you have a Controller View that registers itself with a Store. The Controller View is your smart component, it gets it state from the Store(s) and usually it renders child components, dumb components, where it passes (some of) the state as properties to it's child(ren).
When for instance you're fetching data from a server, you need to trigger an Action Creator, this Action Creator will then call a Web API utility that performs the actual request. When successful, the Web API utility will call a Server Action Creator that dispatches an action containing the payload received from the server. Any registered Store will then process this action and emit a change event. This way any Controller View interested in a Store, will be notified by it when the Store's data has changed. The Controller View will update it's state and re render itself and any child(ren) to display correct data.
This means that you can call an Action Creator to for instance fetch data from the server in the componentDidMount (note that this hook is only executed once though!) method of the Controller View.
Initially the Controller View will ask the store for data and get, for example, an empty array, which will be set as the Controller View's state and the Controller View will render something empty. Then after the data has been fetched, the Controller View (who is notified by the Store) will again retrieve the data from the Store, only now it will not be empty. The Controller View updates it's state accordingly, triggering a re render and displaying the appropriate data.
The essence of this is captured in this minimal (pseudo) code:
// Action Creator: actions/data.js
import { fetchData } from '../utils/server';
export function fetch() {
fetchData();
}
// Server Action Creator: actions/data-server.js
import dispatcher from '../dispatcher';
export function receiveData(data) {
dispatcher.dispatch({
type: 'RECEIVE_DATA',
payload: data
});
}
// Web API Utility: utils/server.js
import request from 'request';
import { receiveData } from '../actions/data-server';
export function fetchData() {
request.get('https://api.com/v1/data', receiveData);
}
// Store: stores/store.js
import dispatcher from '../dipatcher';
import { EventEmitter } from 'events';
const CHANGE_EVENT = 'change';
let __data__ = [];
const Store = Object.assign({}, EventEmitter.prototype, {
emitChange () {
this.emit(CHANGE_EVENT);
},
addChangeListener (callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener (callback) {
this.removeListener(CHANGE_EVENT, callback);
},
getData () {
return __data__;
}
});
Store.dispatchToken = dispatcher.register(function (action) {
switch (action.type) {
case 'RECEIVE_DATA':
__data__ = action.payload;
Store.emitChange();
break;
}
});
export default Store;
// Controller View: components/List.jsx
import React from 'react';
import Store from '../stores/Store');
import { fetch } from '../actions/data';
var __getState = function () {
return {
data: Store.getData()
};
};
export default React.createClass({
getInitialState () {
return __getState();
},
componentWillMount () {
fetch();
},
componentDidMount () {
Store.addChangeListener(this._onChange);
},
componentWillUnMount () {
Store.removeChangeListener(this._onChange);
},
_onChange () {
this.setState( __getState() );
},
render () {
const { data } = this.state;
return (
<div className="list">
<ul>
{ data.map(dataItem => <li>{ dataItem.name }</li> )}
</ul>
</div>
);
}
});
Check out more detailed flux examples here.
I actually don't want a view to call for the data. In my opinion a
view should be as stupid as possible...
It's good practice to distinguish between smart and dumb components. The idea is that smart components hold state and call actions, while the dumb components have no dependencies on the rest of the application. This creates a clear separation of concerns and allows for better reusability and testability of your components. Read more about it here.
Also there are some interesting alternatives besides flux. For instance there is redux. It uses a single immutable state Object (i.e. Store) where a reducer (pure function) is only allowed to modify application state by means of actions.
And definitely check out react-refetch if you're mostly fecthing and rendering read-only data from a server.
A common way to do this is to initialize the async call in the view code's componentDidMount() method, and render it properly.
The code might be different for different flux implementations but you get the idea
getInitialState() {
return {
rows: MyStore.getState().rows
}
}
componentDidMount() {
this.listenTo(MyStore, this.onChange)
MyActions.fetchData()
}
onChange() {
this.setState(this.getInitialState())
}
....
render() {
if (!this.state.rows) {
return <span />
}
return <Table rows={this.state.rows}>
}
As for the container components, it's totally up to you to use it or not.

Categories