I'm writing a reaction web application with firebase backend. Based on field query of one document, I wanted to access another document.
I'm only able to return the query and directly use it for accessing the document. It's making a call to the function and query over and over again.
import React, { Component } from 'react';
import { compose } from 'recompose';
import { withAuthorization } from '../Session';
import { withStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import './Image/1.jpeg'
import Album from './Album.js';
const styles = theme => ({
root: {
...theme.mixins.gutters(),
paddingTop: theme.spacing.unit * 2,
paddingBottom: theme.spacing.unit * 2,
},
});
class ChildPage extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
items: [],
};
this.classes = props;
}
componentDidMount() {
this.setState({ loading: true });
let query = [];
this.unsub = this.props.firebase.users().doc(this.props.firebase.userId()).get().then(doc => {
query.push(doc.data().LinkedUsername)
const lU = this.props.firebase.users().where("email", "==", query[0])
lU.get().then(snapshot => {
console.log(snapshot.docs[0].id)
})
})
this.unsubscribe = this.props.firebase
.users().doc(this.props.firebase.userId()).collection('tasks')
.onSnapshot(snapshot => {
let items = [];
snapshot.forEach(doc =>
(doc.data().status === false) ?
items.push({ ...doc.data(), uid: doc.id })
:
null
);
this.setState({
items,
loading: false,
});
});
}
componentWillUnmount() {
this.unsubscribe();
this.unsub();
}
removeItem(itemId) {
const itemRef = this.props.firebase.users().doc(`${itemId}`);
itemRef.remove();
}
render() {
return (
<div className='background'>
<div className='topBar'>
</div>
<Paper className={ this.classes.root } elevation={ 1 }>
<Album cards={ this.state.items } />
</Paper>
</div>
);
}
}
const condition = authUser => !!authUser;
export default compose(
withAuthorization(condition),
withStyles(styles),
)(ChildPage)
I want the query to run one time and assign the return value to a variable. Then be able to use that value to access them and load the documents.
Currently, this gives me a document id in the console. And if I want to use that id, I take the next two "})" brackets and put them before
"
}
componentWillUnmount() {"
and take everything inside the console i.e. "snapshot.docs[0].id" and put it inplace of "this.props.firebase.userId()" in the doc of this.unsubscribe.
But it's what calls the function over and over and gives the error,
"Warning: Encountered two children with the same key, [object Object]. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version."
Related
I need to click twice on a button linked to the current page so i can get the data fetched from the api to render. I am using nivo / charts to visualize my data.
The component fetches the company list from the api, and a second fetch loops through every result fetching data for every distinct company.
On first try, the company list were to fetch on the parent component, and a fetch request would take place for every child component thereafter
(parent=list of chart components, child=Company Chart), but on the pagination process it did not render properly so I had to uplifted the state to the parent component, the problem this time was that the parent component did not render on first click, I had to double click for example link button so that the parent component would render.
I thought the problem might be occurring since there might had been a missynchronization with the componentDidMount order of actions since I was sure that the first and second data fetching (first being the company get request and second distinct company get request), were executing simultaneously rather than one after the other. So I directed to redux and architectured my application to redux rules. It did not resolve anything and still requires to double click on a link so that the rendering would take place.
Now I feel like I would need to add some await/async rules for the api fetching process but I am not sure whether that would work or not, so I would really appreciate getting a second opinion on how to solve this little problem because it has been bugging me for weeks.
my Reducer:
import { FETCH_COMPANIES } from '../actions/types';
const initialState = {
next : null,
prev : null,
items : [],
item : [],
}
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_COMPANIES:
return {
...state,
items : action.payload.companies,
next : action.payload.next,
prev : action.payload.prev,
}
default:
return state;
}
}
my Store.js:
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware)
)
)
export default store;
my Actions:
import axios from 'axios';
import { FloatType } from 'three';
import { FETCH_COMPANIES } from './types';
export const fetchAllData = (url) => dispatch => {
fetch(url)
.then(res => res.json())
.then(
posts =>
dispatch({
type : FETCH_COMPANIES,
payload : FetchCall(posts),
})
)
}
function FetchCall(res) {
let next;
let prev;
try {
next = res.next;
}
catch(err) {
console.log(err)
}
try {
prev = res.previous;
}
catch(err) {
console.log(err)
}
const CompanyArray = Array()
res.results.map(element => {
axios.get(`https://API/${element.symbol}/`).then((res) => {
const DataGroup = handleChartData(res.data)
CompanyArray.push({
'name' : element.symbol,
'data' : DataGroup,
})
})
});
const ALL_DATA = {
'next' : next,
'prev' : prev,
'companies' : CompanyArray,
}
return ALL_DATA;
}
function handleChartData(data) {
DataGroup = Object()
return DataGroup;
}
And my Component:
import React, { useState} from 'react';
import { Card, Row, Col, Button } from 'antd';
import Chart from '../components/Chart';
import DetailCompany from './CompanyDetail';
import { connect } from 'react-redux';
import { fetchAllData } from '../actions/chartActions';
import PropTypes from 'prop-types';
class CompanyList extends React.Component {
constructor(props) {
super(props);
this.state = {
charts : this.props.charts
}
}
componentWillMount() {
try {
this.props.fetchAllData("https://API/company/")
}
catch(err) {
console.log(err)
}
};
prevPage = () => {
let toPage = this.props.prev
this.props.fetchAllData(toPage)
}
nextPage = () => {
let toPage = this.props.next
this.props.fetchAllData(toPage)
}
render() {
const chartItems = this.state.charts.map(chart => (
<Col style={{margin:'0 0 75px 0'}} span={12} key={chart.name}>
<h1 style={{lineHeight:'2em', margin:'0 0 0 70px'}}>{chart.name}</h1>
<div className="chart-block">
<Chart symbol={chart.name}
data={chart.data.chartData}
>
</Chart>
</div>
</Col>
));
return (
<Card>
<Row>
{chartItems}
</Row>
<Row>
<Button disabled={(this.props.prev ? false : true )} onClick={() => {this.prevPage()}}>Previous</Button>
<Button onClick={() => {this.nextPage()}}>Next</Button>
</Row>
</Card>
)
}
}
CompanyList.propTypes = {
fetchAllData : PropTypes.func.isRequired,
charts : PropTypes.array.isRequired,
}
const mapStateToStore = state => ({
prev : state.charts.prev,
next : state.charts.next,
charts : state.charts.items,
});
export default connect(mapStateToStore, { fetchAllData })(CompanyList);
I would genuinely appreciate if anyone could help me to get around this problem and understand it to prevent further misdirection or reoccurrence. Thank you.
Your fetch thunk is not quite right. In particular, this line:
payload : FetchCall(posts),
FetchCall is asynchronous, but you aren't waiting for it to finish before dispatching. Within the FetchCall you are returning ALL_DATA with an empty CompanyArray before the axios calls finish.
You need to complete all fetch calls before returning or dispatching anything. You can do this with Promise/then, but I find it easier with async/await. Either way you need Promise.all to resolve the entire array. (Also I don't know why you use axios in one place and fetch in the other?).
// helper function to fetch the data for one company
const getCompanyData = async (symbol) => {
const res = await axios.get(`https://API/${symbol}/`);
return {
name: symbol,
data: res.data,
}
}
export const fetchAllData = (url) => async (dispatch) => {
const res = await axios.get(url);
const posts = res.data;
const {next, prev, results} = posts;
const companyArray = await Promise.all(
results.map( element => getCompanyData(element.symbol) )
);
dispatch({
type : FETCH_COMPANIES,
payload: {
next,
prev,
companyArray,
}
});
}
One of the issue that I noticed that the fetchcall is an async request so I think the companyarray would be a blank array in the payload. Are you sure you are getting payload.companies when FETCH_COMPANIES is dispatched?
I use MobX to control my ReactJS state/components and I also use an async call through MobX in order to retrieve the data, this is typically called in my header throuhg componentDidMount().
(I already know my code isn't the cleanest and most likely has errors, cut me some slack as I'm learning/coding this completely on my own at this point with no educational background in programming)
import React from 'react';
import { Layout, Row, Col, Menu, Avatar, Tag } from 'antd';
import { inject, observer } from 'mobx-react';
import Icon from '#ant-design/icons';
const { Header } = Layout;
const { SubMenu } = Menu;
#inject('store')
#observer
class PageHeader extends React.Component {
componentDidMount() {
this.props.store.getOrders();
}
Say for instance I was not in the Application, but I was still logged in through my LocalStorage data, and I went to a page "http://localhost/orders/123456". 123456 would be my order ID and this page would display it's details. Now considering I was not on the page, the DOM wasn't rendered, right? Right... But I'm still logged in through my LocalStorage, so when I visit the page - it's rendering blank because MobX has to wait for the API call to retrieve the data. I need to be able to pull this data and make sure it's rendered on the page, so I some how need the API to be retrieve before rendering to ensure it's pull the data out of MobX with the specified OrderID, in this case 123456.
Below is two ways I've made my componentDidMount
#inject('store')
#observer
class LoadPage extends React.Component {
state = {
visible: false,
ordernum: this.props.match.params.id,
orderkey: null,
}
componentDidMount() {
document.title = this.props.match.params.id;
route_ordernum = this.props.match.params.id;
if (this.props.store.orders.length === 0) {
fetch('http://localhost:5000')
.then(res1 => res1.json())
.then(data => this.props.store.setOrders(data))
.then(this.setState({
orderkey: this.props.store.orders.filter(order => order._id.includes(route_ordernum)).map((data, key) => { return data.key })
}))
}
if (this.props.store.orders.length > 0) {
this.setState({
orderkey: this.props.store.orders.filter(order => order._id.includes(route_ordernum)).map((data, key) => { return data.key })
})
}
console.log(this.state.orderkey)
}
render() {
Example #2
componentDidMount() {
document.title = this.props.match.params.id;
route_ordernum = this.props.match.params.id;
if (this.props.store.orders.length === 0) {
this.props.store.getOrders().then(dataloads => {
this.setState({
orderkey: this.props.store.orders.filter(order => order._id.includes(route_ordernum)).map((data, key) => { return data.key })
})
})
}
if (this.props.store.orders.length > 0) {
this.setState({
orderkey: this.props.store.orders.filter(order => order._id.includes(route_ordernum)).map((data, key) => { return data.key })
})
}
console.log(this.state.orderkey)
}
I'm just passing through my MobX and using two separate classes to create my state now.
#inject('store')
#observer
class LoadMain extends React.Component {
render() {
return(
this.props.store.orders.length === 0 ? <Content style={{ backgroundColor: "#ffffff" }}><center><Spinner /></center></Content> : <OrderPage ordernum={this.props.match.params.id} />
);
}
}
I am new to MST and is having a hard time finding more examples with async actions. I have an api that will return different data depending on the params you pass to it. In this case, the api can either return an array of photos or tutorials. I have set up my initial values for the store like so:
data: {
photos: [],
tutorials: []
}
Currently, I am using applySnapshot to update the store and eventually, that will trigger a re-render of my React component. In order to display both photos and tutorials, I need to call the api twice (Once with the params for photos and the second time for tutorials). I am running into an issue where the snapshot from the first update shows that photos and tutorials have the same values and only on the second update, do I get the correct values. I am probably misusing applySnapshot to re-render my React components. I would like to know the better/proper way of doing this. What is the best way to re-render my React components after the api has yielded a repsonse. Any suggestions are much appreciated
I have set up my store like this:
import { RootModel } from '.';
import { onSnapshot, getSnapshot, applySnapshot } from 'mobx-state-tree';
export const setupRootStore = () => {
const rootTree = RootModel.create({
data: {
photos: [],
tutorials: []
}
});
// on snapshot listener
onSnapshot(rootTree, snapshot => console.log('snapshot: ', snapshot));
return { rootTree };
};
I have created the following model with an async action using generators:
import {types,Instance,applySnapshot,flow,onSnapshot} from 'mobx-state-tree';
const TestModel = types
.model('Test', {
photos: types.array(Results),
tutorials: types.array(Results)
})
.actions(self => ({
fetchData: flow(function* fetchData(param) {
const results = yield api.fetch(param);
applySnapshot(self, {
...self,
photos: [... results, ...self.photos],
tutorials: [... results, ...self.tutorials]
});
})
}))
.views(self => ({
getPhoto() {
return self.photos;
},
getTutorials() {
return self.tutorials;
}
}));
const RootModel = types.model('Root', {
data: TestModel
});
export { RootModel };
export type Root = Instance<typeof RootModel>;
export type Test = Instance<typeof TestModel>;
React component for Photos.tsx
import React, { Component } from 'react';
import Spinner from 'components/Spinner';
import { Root } from '../../stores';
import { observer, inject } from 'mobx-react';
interface Props {
rootTree?: Root
}
#inject('rootTree')
#observer
class Photos extends Component<Props> {
componentDidMount() {
const { rootTree } = this.props;
if (!rootTree) return null;
rootTree.data.fetchData('photo');
}
componentDidUpdate(prevProps) {
if (prevProps.ctx !== this.props.ctx) {
const { rootTree } = this.props;
if (!rootTree) return null;
rootTree.data.fetchData('photo');
}
}
displayPhoto() {
const { rootTree } = this.props;
if (!rootTree) return null;
// calling method in MST view
const photoResults = rootTree.data.getPhoto();
if (photoResults.$treenode.snapshot[0]) {
return (
<div>
<div className='photo-title'>{'Photo'}</div>
{photoResults.$treenode.snapshot.map(Item => (
<a href={photoItem.attributes.openUrl} target='_blank'>
<img src={photoItem.url} />
</a>
))}
</div>
);
} else {
return <Spinner />;
}
}
render() {
return <div className='photo-module'>{this.displayPhoto()}</div>;
}
}
export default Photos;
Similarly, Tutorials.tsx is like so:
import React, { Component } from 'react';
import Spinner from '';
import { Root } from '../../stores';
import { observer, inject } from 'mobx-react';
interface Props {
rootTree?: Root;
}
#inject('rootTree')
#observer
class Tutorials extends Component<Props> {
componentDidMount() {
if (this.props.ctx) {
const { rootTree } = this.props;
if (!rootTree) return null;
rootTree.data.fetchData('tuts');
}
}
componentDidUpdate(prevProps) {
if (prevProps.ctx !== this.props.ctx) {
const { rootTree } = this.props;
if (!rootTree) return null;
rootTree.search.fetchData('tuts');
}
}
displayTutorials() {
const { rootTree } = this.props;
if (!rootTree) return null;
// calling method in MST view
const tutResults = rootTree.data.getTutorials();
if (tutResults.$treenode.snapshot[0]) {
return (
<div>
<div className='tutorials-title'>{'Tutorials'}</div>
{tutResults.$treenode.snapshot.map(tutorialItem => (
<a href={tutorialItem.attributes.openUrl} target='_blank'>
<img src={tutorialItem.url} />
</a>
))}
</div>
);
} else {
return <Spinner />;
}
}
render() {
return <div className='tutorials-module'>{this.displayTutorials()}</div>;
}
}
export default Tutorials;
Why are you using applySnapshot at all in this case? I don't think it's necessary. Just assign your data as needed in your action:
.actions(self => ({
//If you're fetching both at the same time
fetchData: flow(function* fetchData(param) {
const results = yield api.fetch(param);
//you need cast() if using Typescript otherwise I think it's optional
self.photos = cast([...results.photos, ...self.photos])
//do you really intend to prepend the results to the existing array or do you want to overwrite it with the sever response?
self.tutorials = cast(results.tutorials)
})
}))
Or if you need to make two separate requests to fetch your data it's probably best to make it two different actions
.actions(self => ({
fetchPhotos: flow(function* fetchPhotos(param) {
const results = yield api.fetch(param)
self.photos = cast([... results, ...self.photos])
}),
fetchTutorials: flow(function* fetchTutorials(param) {
const results = yield api.fetch(param)
self.tutorials = cast([... results, ...self.tutorials])
}),
}))
Regardless, it doesn't seem like you need applySnapshot. Just assign your data in your actions as necessary. There's nothing special about assigning data in an async action.
I am rendering a card in a parent component for every post that a user has. In the card, all of the data is passed down via props. I have a delete axios call that works, however I have to manually refresh the page for the page to show updates.
Any way I can have it manually update the UI?
// DASHBOARD.JS
if (this.state.posts.length > 0 && this.state.loaded === true) {
const posts = this.state.posts;
content = posts.map(post => (
<Card
key={post._id}
author={post.author}
title={post.title}
date={post.date}
body={post.body}
id={post._id}
/>
));
// CARD.JS
deleteOnClick = e => {
axios
.delete('http://localhost:5000/auth/deletePost', {
params: {
id: this.props.id
}
})
.then(res => {
console.log(res);
})
.catch(err => console.log(err));
};
I think you have two problems to fix in order to make this pattern work.
First thing first: avoid defining business logic in components used only for presentational purposes (have a read here).
So in Card component there should be no explicit definition of the deleteOnClick method, while it should receive it from above in a dedicated prop of type func.
Second thing: the list component should handle the logic of deleting items from the list through the axios call and in the then statement you should think of a way to update the list items you are using to render Cards.
Examples in pseudocode:
List Component
import React from 'react';
import Card from './Card';
export default class List extends PureComponent {
state = {
items: [],
error: null,
}
componentDidMount() {
// add axios call to retrieve items data
}
deleteItemHandler = () => {
axios
.delete('http://localhost:5000/auth/deletePost', {
params: {
id: this.props.id
}
})
.then(res => {
this.setState({
items: res.json(),
})
})
.catch(err => {
this.setState({
error: err,
})
});
};
}
render() {
const { items } = this.state;
return (
<div>
{items.map(item => (
<Card
{...item}
onClick={this.deleteItemHandler}
/>
))}
</div>
)
}
}
Card component:
import React from 'react';
import PropTypes from 'prop-types';
export default class Card extends PureComponent {
static propTypes = {
title: PropTypes.string,
// other props
onClick: PropTypes.func.isRequired,
}
// other things in this class
render() {
const { onClick, title } = this.props;
return (
<div onClick={onClick}>
<h1>{title}</h1>
</div>
)
}
}
Once you get familiar with concept of separating logic and presentation you can start introducing redux and do things at another level :)
Hope this helps!
Stack : React16, ES6, Redux
I'm currently unable to figure what's wrong here. The goal here is to add dynamically an infinite number of components (one by one) when clicking on an add button.
I need to make them appear, if possible by pair or more, e.g. if I click on the ADD button, there should be 2 fields appearing each time (one select field and one textfield at the same time, for ex)
I'm able to make the components appear, with the help of Redux, and I'm also able to manage the datas correctly (everything's wired on the back of the app)
THE PROBLEM HERE :
When trying to type text in an input field, it's ALWAYS losing the focus. I've seen that each time I update my props, the whole component named MultipleInputChoiceList is mounted again, and that each fields are re-created anew. That's what I need to fix here :
EDIT : The MultipleInputChoiceList component is mounted via a Conditional Rendering HOC (It takes some values and check if they are true, if they are, it's rendering the component without touching the whole form)
ConditionalRenderingHOC.js
import React from 'react'
import {connect} from 'react-redux'
import _ from 'lodash'
const mapStateToProps = state => {
return {
form: state.form.form
}
}
const mapDispatchToProps = dispatch => {
return {
}
}
/**
* HOC Component to check conditional rendering on form component, using requireField property
* To be enhanced at will
*/
export default (WrappedComponent, formItem = {}) => {
class ConditionalRenderingHOC extends React.Component {
componentWillMount() {
//Check if all informations are available
if (formItem.requireField !== undefined) {
const requireField = formItem.requireField
if (requireField.value !== undefined &&
requireField.name !== undefined &&
requireField.field !== undefined &&
requireField.property !== undefined) {
//If everything's here let's call canBeRendered
this.canBeRendered()
}
}
}
//Check if the count of fetched values is directly linked to the number of fetched config asked, if true, return the same number
canBeRendered() {
formItem.requireField.isRendered = false
let required = formItem.requireField
let isEqual = false
if (this.props.form[required.field] !== undefined) {
let field = this.props.form[required.field]
_.forEach(field.value, (properties, index) => {
if (properties[required.name] !== undefined) {
if (properties[required.name] === required.value) {
if (properties[required.property] === required.isEqualTo) {
formItem.requireField.isRendered = true
isEqual = true
}
}
}
})
}
return isEqual
}
render() {
let isConditionMet = this.canBeRendered()
let render = null
if (isConditionMet === true) {
render = <WrappedComponent items={formItem}/>
}
return (<React.Fragment>
{render}
</React.Fragment>)
}
}
return connect(mapStateToProps, mapDispatchToProps)(ConditionalRenderingHOC)
}
The code
//Essentials
import React, { Component } from 'react'
import _ from 'lodash'
//Material UI
import TextField from 'material-ui/TextField'
import IconButton from 'material-ui/IconButton'
import AddBox from 'material-ui/svg-icons/content/add-box'
//Components
import SelectItemChoiceList from '../form/SelectItemChoiceList'
import TextFieldGeneric from './TextFieldGeneric'
//Redux
import { connect } from 'react-redux'
import { createNewField } from '../../../actions/formActions'
const mapStateToProps = (state) => {
return {
form: state.form.form
}
}
const mapDispatchToProps = (dispatch) => {
return {
createNewField: (field, state) => dispatch(createNewField(field, state))
}
}
class MultipleInputChoiceList extends Component {
constructor(props) {
super(props)
this.state = {
inputList: [],
}
}
onAddBtnClick() {
const name = this.props.items.name
/**Create a new field in Redux store, giving it some datas to display */
this.props.createNewField(this.props.form[name], this.props.form)
}
render() {
const name = this.props.items.name
/**I think the error is around this place, as it always re-render the same thing again and again */
const inputs = this.props.form[name].inputList.map((input, index) => {
switch(input) {
case 'selectfield': {
return React.createElement(SelectItemChoiceList, {
items: this.props.form[name].multipleField[index],
key:this.props.form[name].multipleField[index].name
})
}
case 'textfield': {
return React.createElement(TextFieldGeneric, {
items: this.props.form[name].multipleField[index],
index:index,
key:this.props.form[name].multipleField[index].name
})
}
default: {
break
}
}
})
return (
<div>
<IconButton onClick={this.onAddBtnClick.bind(this)}>
<AddBox />
</IconButton>
{inputs}
</div>
)
}
}
const MultipleInputChoiceListRedux = connect(mapStateToProps, mapDispatchToProps)(MultipleInputChoiceList)
export default MultipleInputChoiceListRedux
And the TextField used here :
TextFieldGeneric.js
//Essentials
import React, { Component } from 'react';
//Components
import TextField from 'material-ui/TextField'
//Redux
import { connect } from 'react-redux'
import { validateField, isValid } from '../../../actions/formActions'
const mapStateToProps = (state) => {
return {
form: state.form.form
}
}
const mapDispatchToProps = (dispatch) => {
return {
validateField: (field) => dispatch(validateField(field)),
isValid: () => dispatch(isValid())
}
}
class TextFieldGeneric extends Component {
constructor(props) {
super(props)
this.state = {
form: {},
field: {},
index: 0
}
}
componentWillMount() {
console.log(this.props)
//first, let's load those dynamic datas before rendering
let form = this.props.form
let index = this.props.index
/** Check if there's a correctly defined parent in form (taken from the name) */
let matchName = /[a-zA-Z]+/g
let origin = this.props.items.name.match(matchName)
//form.company.value = this.getCompaniesFormChoice()
this.setState({form: form, field: form[origin], index: index})
}
//setState and check validationFields if errors
handleFieldChange(event){
const name = event.target.name
const value = event.target.value
//Change value of state form field
const item = this.props.items
item.value = value
//validate each fields
this.props.validateField(item)
//validate form
this.props.isValid()
event.preventDefault()
}
render() {
const index = this.state.index
console.log(index)
return (
<React.Fragment>
<TextField
key={index}
floatingLabelText={this.state.field.multipleField[index].namefield}
name={this.state.field.multipleField[index].namefield}
floatingLabelFixed={true}
value = {this.state.field.multipleField[index].value}
onChange = {this.handleFieldChange.bind(this)}
errorText={this.state.field.multipleField[index].error === 0 ? '' : this.state.field.multipleField[index].error}
/>
</React.Fragment>
)
}
}
const TextFieldGenericRedux = connect(mapStateToProps, mapDispatchToProps)(TextFieldGeneric)
export default TextFieldGenericRedux
I also do understand that a part of the problem lies in the render method of the parent class (MultipleInputChoiceList.js) ...
Any help or comments REALLY appreciated!
As of now, I've not come to a real answer to that question, as it's a structural problem in my data (When updating a field via a Redux action, it's re-rendering the whole component). Maybe stock those data elsewhere would be a better option.
I've only used a onBlur method on the field, that dismiss the validation I want to do on each user input, but for the moment I could not think to another viable solution.
So far, the TextFieldGeneric.js looks like that :
//setState and check validationFields if errors
handleFieldChange(event){
this.setState({value: event.target.value})
event.preventDefault()
}
handleValidation(event){
const value = this.state.value
//Change value of state form field
const item = this.props.items
item.value = value
//validate each fields
this.props.validateField(item)
//validate form
this.props.isValid()
}
render() {
return (
<React.Fragment>
<TextField
name='name'
floatingLabelFixed={true}
value = {this.state.value}
onChange = {this.handleFieldChange.bind(this)}
onBlur = {this.handleValidation.bind(this)}
/>
</React.Fragment>
)
}
If anyone have another solution, I'll gladly hear it !