I am new to the react js. Here , I have a table which has checkbox functionality.In this I am creating a dynamic state and I want to pass that to the child component where the actual table is
My code is like ,
JobList.js
class JobList extends React.Component {
constructor(props) {
this.state = {
isCheckd: false
}
handleCheckBox = () => {
this.setState({
isCheckd: !this.state.isCheckd
}, () => {
this.props.jobs.forEach((item) => this.setState({ [item.jdName]: this.state.isCheckd }))
});
}
handleTableCheckboxChange = (e) => {
this.setState({
[e.target.name]: e.target.checked
}, () => {
const uncheckedItems = this.props.jobs.filter((item) => !this.state[item.jdName])
this.setState({
isCheckd: uncheckedItems.length === 0 ? true : false
});
});
}
return() (
<table className="table" id="actions-table">
<tbody>
<tr>
<td className="text-right mr-1"><input type="checkbox" checked={this.state.isCheckd} onChange={this.handleCheckBox} />
</td> </tr></tbody></table>
}
<UserJobsTabel
jobList={filteredList}
sortAscending={this.sortData}
sortCountAndScoreAscending={this.sortNumbersAscending}
addNewRow={this.addNewRow}
isRowAddingEditorVisible={this.props.isRowAddingEditorVisible}
removeRow={this.removeRow}
checked={this.state.isCheckd}
handleTableCheckboxChange={this.handleTableCheckboxChange} />
}
const mapStateToProps = (state) => {
return {
jobs: state.UserJobs.jobList,
}
}
Now, In this code I am trying to check and uncheck the checkboxes.
this.state[item.jdName] so this state is getting generated for the each element in the array.
Now, I want to pass it to the UserJobsTable .
const UserJobsTable = (props) => {
return (
<tbody className="text-center">
{props.isRowAddingEditorVisible && <RowAddingEditor removeRow={props.removeRow} />}
{props.jobList && props.jobList && props.jobList.length > 0 && props.jobList.map((item, key) => {
return (
<tr key={key}>
<td align="center"> <input type="checkbox" name={props.key} checked={props[item.jdName]} onChange={props.handleTableCheckboxChange} /></td></tr></tbody> ) }
handleTableCheckboxChange = (e) => {
this.props.jobs.filter((item) => this.setState(prevState => ({
dynamicProp: {
...prevState.dynamicProp,
[item.jdName]: e.target.name === [item.jdName] ? true : false
}
})
))
}
I want to set it to the checkbox . So, I am not getting a way through which I can pass this state. is there any way I can do this ?
You could wrap the dynamic property under a key and pass it.
this.state = {
isCheckd: false,
dynamicProp: {}
}
handleCheckBox = () => {
this.setState({
isCheckd: !this.state.isCheckd
}, () => {
this.props.jobs.forEach((item) =>
this.setState(prevState => {
dynamicProp: {
...prevState.dynamicProp,
[item.jdName]: prevState.isCheckd
}
})
)
});
}
In all your setState, you do
this.setState(prevState => {
dynamicProp: {
...prevState.dynamicProp,
[item.jdName]: prevState.isCheckd
}
})
and to pass it to the child accordingly.
Related
When i call this function
getQuestions = () => {
this.setState({ loading: true })
const { data } = this.props
axios
.get(data.questions)
.then((res) => {
this.setState({
loading: false,
questions: res.data,
})
this.initialQuestions = res.data
})
.catch((err) =>
this.setState({
loading: false,
questions: [],
})
)
}
it updates the array questions in state and array initialQuestions variable in constructor. The state questions represents the values form inputs. The inputs are handled in child component with this code
onChange = (e) => {
const { hasChanged, setQuestions } = this.props
// Update questions
let questions = this.props.questions
questions[e.target.getAttribute('data-id')][e.target.name] =
e.target.value
setQuestions(questions)
}
setQuestions is passed in props as setQuestions={(state) => this.setState({ questions: state })}
So when i change the value of inputs the onChange function is called and it changes the parent component questions in state. But the parent variable this.initialQuestions also is being changed to the questions value from state, but I don't know why
Edit:
That's the code you should be able to run
const { Component } = React;
const Textarea = "textarea";
const objectsEquals = (obj1, obj2) =>
Object.keys(obj1).length === Object.keys(obj2).length &&
Object.keys(obj1).every((p) => obj1[p] === obj2[p])
class QuestionList extends React.Component {
static propTypes = {
questions: PropTypes.array,
removeQuestion: PropTypes.func.isRequired,
hasChanged: PropTypes.func.isRequired,
setQuestions: PropTypes.func.isRequired,
}
constructor(props) {
super(props)
this.questions = props.questions
this.onChange = this.onChange.bind(this)
}
onChange = (e) => {
const { hasChanged, setQuestions } = this.props
// Update questions
let questions = this.props.questions
questions[e.target.getAttribute('data-id')][e.target.name] =
e.target.value
setQuestions(questions)
if (hasChanged && this.questions.length > 0) {
// array of booleans, true if object has change otherwise false
const hasChangedArray = this.props.questions.map(
(_, index) =>
!objectsEquals(
this.questions[index],
this.props.questions[index]
)
)
console.log("hasChangedArray = ", hasChangedArray)
console.log("this.questions[0] = ", this.questions[0])
console.log("this.props.questions[0] = ", this.props.questions[0])
// If true in array than the form has changed
hasChanged(
hasChangedArray.some((hasChanged) => hasChanged === true)
)
}
}
render() {
const { removeQuestion, questions } = this.props
const questionList = questions.map((question, index) => (
<div className="card" key={index}>
<div className="card__body">
<div className="row">
<div className="col-sm-7">
<div className="form-control">
<label className="form-control__label">
Question:
</label>
<input
type="text"
id={`question-${index}`}
data-id={index}
onChange={this.onChange}
name="question"
value={
this.props.questions[index].question
}
className="form-control__input form control__textarea"
placeholder="Pass the question..."
rows="3"
/>
</div>
<div className="form-control">
<label className="form-control__label">
Summery:
</label>
<Textarea
id={`summery-${index}`}
data-id={index}
onChange={this.onChange}
name="summery"
value={this.props.questions[index].summery}
className="form-control__input form-control__textarea"
placeholder="Pass the summery..."
rows="3"
/>
</div>
</div>
</div>
</div>
</div>
))
return questionList
}
}
class Questions extends React.Component {
constructor(props) {
super(props)
this.initialQuestions = []
this.state = {
loading: true,
questions: [],
hasChanged: false,
}
this.getQuestions = this.getQuestions.bind(this)
this.resetForm = this.resetForm.bind(this)
}
resetForm = () => {
console.log("this.initialQuestions =", this.initialQuestions)
this.setState({
questions: this.initialQuestions,
hasChanged: false,
})
}
getQuestions = () => {
this.setState({ loading: true })
const { data } = this.props
// axios
// .get(data.questions)
// .then((res) => {
// this.setState({
// loading: false,
// questions: res.data,
// })
// this.initialQuestions = res.data
// })
// .catch((err) =>
// this.setState({
// loading: false,
// questions: [],
// })
// )
// You can't do a database request so here is some example code
this.setState({
loading: false,
questions: [
{
question: 'example-question',
summery: 'example-summery',
},
{
question: 'example-question-2',
summery: 'example-summery-2',
},
],
})
this.initialQuestions = [
{
question: 'example-question',
summery: 'example-summery',
},
{
question: 'example-question-2',
summery: 'example-summery-2',
},
]
}
componentDidMount = () => this.getQuestions()
render() {
const { loading, questions, hasChanged } = this.state
if (loading) return <h1>Loading...</h1>
return (
<form>
<QuestionList
questions={questions}
hasChanged={(state) =>
this.setState({ hasChanged: state })
}
setQuestions={(state) =>
this.setState({ questions: state })
}
/>
<button
type="reset"
onClick={this.resetForm}
className={`btn ${
!hasChanged
? 'btn__disabled'
: ''
}`}
>
Cancel
</button>
<button
type="submit"
className={`btn btn__contrast ${
!hasChanged
? 'btn__disabled'
: ''
}`}
>
Save
</button>
</form>
)
}
}
ReactDOM.render(<Questions />, document.querySelector("#root"));
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/prop-types#15/prop-types.min.js"></script>
<div id="root"></div>
Both state questions and class variable initialQuestions hold reference of res.data. Now when you update questions in onChange method, you are updating it by reference i.e directly mutating it and hence the class variable is also updated
You must not update it by reference but clone and update like below
onChange = (e) => {
const { hasChanged, setQuestions } = this.props
// Update questions
let questions = this.props.questions
questions = questions.map((question, idx) => {
if(idx === e.target.getAttribute('data-id')) {
return {
...question,
[e.target.name]: e.target.value
}
}
return question;
});
setQuestions(questions)
}
i am trying to convert my class component to a functionnal component but my filters are not working anymore.
the main error is when I want to convert listProducts because I don't know how to define the prevState with useState for that case
how can update the state for case?
this is my code
class component
class ListShopping extends Component{
constructor(props) {
super(props);
this.state = {
size: "",
sort: "",
cartItems: [],
products: [],
filteredProducts: []
};
}
componentDidMount() {
fetch("http://localhost:8000/products")
.then(res => res.json())
.catch(err =>
fetch("db.json")
.then(res => res.json())
.then(data => data.products)
)
.then(data => {
this.setState({ products: data });
this.listProducts();
});
}
listProducts = () => {
this.setState(state => {
if (state.sort !== "") {
state.products.sort((a, b) =>
state.sort === "lowestprice"
? a.price < b.price
? 1
: -1
: a.price > b.price
? 1
: -1
);
} else {
state.products.sort((a, b) => (a.id > b.id ? 1 : -1));
}
if(state.size !== ""){
return {
filteredProducts: state.products.filter(
a => a.availableSizes.indexOf(state.size.toUpperCase()) >= 0
)
}
}
return { filteredProducts: state.products };
});
};
handleSortChange = e => {
this.setState({ sort: e.target.value });
this.listProducts();
};
handleSizeChange = e => {
this.setState({ size: e.target.value });
this.listProducts();
};
render() {
return (
<div className="container">
<h1>E-commerce Shopping Cart Application</h1>
<hr />
<div className="row">
<div className="col-md-9">
<Filter
count={this.state.filteredProducts.length}
handleSortChange={this.handleSortChange}
handleSizeChange={this.handleSizeChange}
/>
<hr />
<Products
products={this.state.filteredProducts}
/>
</div>
</div>
</div>
);
}
}
functionnal component
const ListShopping = () => {
const [data, setData] = useState({
products : [],
filteredProducts : [],
sort : '',
size : ''
})
const {products, filteredProducts, sort, size} = data;
const fetchApi = () => {
axios.get(`http://localhost:8000/products`)
.then(response => response.data)
.then(data => {
setData({...data, products: data});
})
}
const listProducts = () => {
};
const handleSortChange = e => {
setData({...e, sort: e.target.value})
listProducts();
};
const handleSizeChange = e => {
setData({...e, size: e.target.value})
listProducts();
};
useEffect(()=> {
fetchApi()
}, [])
return(
<div className="container">
<h1>E-commerce Shopping Cart Application</h1>
<hr />
<div className="row">
<div className="col-md-9">
<Filter
count={filteredProducts.length}
handleSortChange={handleSortChange}
handleSizeChange={handleSizeChange}
/>
<hr />
<Products
products={filteredProducts}
/>
</div>
</div>
</div>
)
}
Try this
const listProducts = () => {
setData(data => {
if (data.sort !== '') {
data.products.sort((a, b) =>
data.sort === 'lowestprice'
? a.price < b.price
? 1
: -1
: a.price > b.price
? 1
: -1,
);
} else {
data.products.sort((a, b) => (a.id > b.id ? 1 : -1));
}
if (data.size !== '') {
return {
...data,
filteredProducts: data.products.filter(
a => a.availableSizes.indexOf(data.size.toUpperCase()) >= 0,
),
};
}
return { ...data, filteredProducts: data.products };
});
};
Suppose you have a state like this
const [todos, setTodos] = useState([1,2,3,4,5,6,7]);
Method 1:
Now if you want to get prevState from this hook try this way
setTodos(oldTodo => {
oldTodo.push(1000);
setTodos(oldTodo)
})
this oldTodo work the same way the prevState works.
Method 2:
const listProducts = () => {
let oldState = todos
//Now do what you want to do with it. modify the oldTodo
setTodos(oldTodo);
}
I prefer the second method because it gives me more flexibility to change modify the state and if you cannot manage the state properly in the first method or if it finds any bug then it will return undefined so I prefer to work taking the whole state in a temporary variable and after work is done set it
I am just getting acquainted with testing and I have a little problem. I'm trying to test the functions of a component that change state and call functions of a document. In the documentation of Jest and Enzyme, I did not find the right example. Here is a sample code:
class EditableText extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isEditing: false,
currentValue: null
};
this.textAreaRef = React.createRef();
}
onClick = () => {
if (!this.state.isEditing) {
this.setState({ isEditing: true });
setTimeout(() => {
this.textAreaRef.current.focus();
}, 100);
document.addEventListener('keydown', this.onKeyDown);
document.addEventListener('click', this.onOutsideClick);
}
};
onOutsideClick = e => {
if (e.target.value !== this.state.currentValue) {
this.closeEditMode();
}
};
closeEditMode() {
this.setState({ isEditing: false });
document.removeEventListener('keydown', this.onKeyDown);
document.removeEventListener('click', this.onOutsideClick);
}
onKeyDown = e => {
if (e.code === 'Enter') {
this.onUpdateValue();
} else if (e.code === 'Escape') {
this.closeEditMode();
}
};
onUpdateValue = () => {
this.props.onSubmit(this.state.currentValue || this.props.value);
this.closeEditMode();
};
render() {
const { isEditing, currentValue } = this.state;
const { value } = this.props;
return isEditing ? (
<TextArea
className="editable-text__textarea"
ref={this.textAreaRef}
onClick={this.onClick}
value={currentValue === null ? value || '' : currentValue}
onChange={(_e, { value }) => this.setState({ currentValue: value })}
/>
) : (
<div className="editable-text__readonly" onClick={this.onClick}>
<div>{this.props.children}</div>
<div className="editable-text__icon-container">
<Icon name="edit" className="editable-text__icon" />
</div>
</div>
);
}
}
How to test functions, that receive and change state and calls document.addEventListener? Please, help.
UPDATE > Tests i already wrote:
describe('tests of component', () => {
test('Component renders correctly with isEditing=false', () => {
const component = renderer
.create(<EditableText children="I'm text in EditableText" />)
.toJSON();
expect(component).toMatchSnapshot();
});
test('Component changes to isEditing=true when clicked on it', () => {
const component = renderer
.create(<EditableText />);
let tree = component.toJSON();
tree.props.onClick();
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
describe('tests of logic', () => {
test.only('OnClick should change state and add event listeners', ()=> {
const state = { isEditing: false, currentValue: null }
global.document.addEventListener = jest.fn();
EditableText.onClick(() => {
expect(global.document.addEventListener).toHaveBeenCalled();
});;
});
});
I am learning React and I am trying to use a component to show details of a selected item in a table, my problem is that when I click Next in the table (it is paginated), the state got updated and re-render the component and if I do click many times, it enqueue and the details component is changing between all the items I did click on.
I don't know if there is a good practice for doing this kind of things, i tried to search for something like this but nothing yet.
The component I am using (using public API):
import React from 'react';
class Pokemon extends React.Component {
constructor() {
super();
this.state = {
name: "",
abilities: [],
stats: [],
weight: 0,
height: 0,
base_experience: 0,
};
}
fetch_pokemon = () => {
fetch(this.props.pokemon_url)
.then(res => res.json())
.then(res => {
this.setState({
name: res.name,
abilities: res.abilities,
stats: res.stats,
weight: res.weight,
height: res.height,
base_experience: res.base_experience,
});
});
}
render() {
if(this.props.pokemon_url !== ''){
this.fetch_pokemon();
return (
<div>
<h1>{this.state.name}</h1>
Weight: {this.state.weight}<br />
Height: {this.state.height}<br />
Abilities:
<ul>
{this.state.abilities.map(({ability}, index) => {
return (<li key={index}>{ability.name}</li>);
})}
</ul>
</div>
);
}
else{
return (<h3>Choose one pokémon from the list...</h3>);
}
}
}
export default Pokemon;
My main component:
import React, { Component } from 'react';
//import logo from './logo.svg';
import './App.css';
import Pokemon from './Pokemon';
class App extends Component {
constructor() {
const i_pokemons = 25;
super();
this.state = {
pokemons: [],
pokemons_list: [],
pokemon_show_list: [],
p_init: 0,
p_final: i_pokemons,
pagination: i_pokemons,
pages: 0,
status: '',
enable_buttons: false,
current_page: 0,
current_pokemon_url: '',
URL: `https://pokeapi.co/api/v2/pokemon/?limit=99999`
};
}
componentDidMount(){
this.fetch_pokemons();
}
prev(){
this.setState({
current_page: this.state.current_page - 1,
p_init: this.state.p_init - this.state.pagination,
p_final: this.state.p_final - this.state.pagination
}, () => {
this.fetch_new_page();
});
}
next(){
this.setState({
current_page: this.state.current_page + 1,
p_init: this.state.p_init + this.state.pagination,
p_final: this.state.p_final + this.state.pagination
}, () => {
this.fetch_new_page();
});
}
fetch_new_page = () => {
const current_id = (this.state.current_page - 1) * this.state.pagination;
this.setState({
pokemon_show_list: this.state.pokemons_list.slice(current_id, current_id + 25)
});
this.fetch_pokemons();
}
fetch_pokemons = callback => {
this.setState({
status: 'Waiting for the server and retrieving data, please wait...',
enable_buttons: false
});
return new Promise((resolve, reject) => {
fetch(this.state.URL)
.then(res => res.json())
.then(res => {
if(!res.detail){
this.setState({
pokemons: res,
pokemons_list: res.results,
enable_buttons: true,
status: 'Done',
pokemon_show_list: res.results.slice(this.state.p_init, this.state.p_final)
});
if(this.state.pages === 0){
this.setState({
pages: Math.round(this.state.pokemons_list.length / this.state.pagination),
current_page: 1
});
}
resolve(true);
}else{
reject("Error");
this.setState({status: `Error`});
}
})
.catch(error => {
this.setState({status: `Error: ${error}`});
reject(error);
});
});
}
showPokemon({url}){
this.setState({
current_pokemon_url: url
});
}
render() {
console.log("Render");
return(
<div className="general">
<div className="pokemons-info">
{this.state.status !== '' && this.state.status}
<br />
<table className="pokemon-list">
<thead>
<tr>
<th>Name</th>
<th>More info.</th>
</tr>
</thead>
<tbody>
{this.state.pokemon_show_list.map((pokemon, index) => {
return (
<tr className="l" key={index}>
<td>{pokemon.name}</td>
<td><a className="btn btn-secondary" onClick={this.showPokemon.bind(this, pokemon)} href={`#${pokemon.name}`}>More info.</a></td>
</tr>
);
})}
</tbody>
</table>
<button className="btn btn-primary" disabled={this.state.current_page <= 1} onClick={this.prev.bind(this)}>Prev</button>
Page: {this.state.current_page} of {this.state.pages}
<button className="btn btn-primary" disabled={this.state.current_page === this.state.pages} onClick={this.next.bind(this)}>Next</button>
</div>
<Pokemon pokemon_url={this.state.current_pokemon_url}/>
</div>
);
}
}
export default App;
Feel free for giving any advice
I refactored and clean your code a little bit, but I guess the code below aims what you are looking for. (Read more about functional component 'logicless components').
const API = 'https://pokeapi.co/api/v2/pokemon/';
const PAGE_SIZE = 25;
function Status(props) {
return (
<div>{props.value}</div>
)
}
function Pagination(props) {
return (
<div>
<button
onClick={props.onPrevious}
disabled={props.disabled}>
Prev
</button>
<button
onClick={props.onNext}
disabled={props.disabled}>
Next
</button>
</div>
)
}
function Pokemon(props) {
return (
<div>
<h1>{props.pokemon.name}</h1>
Weight: {props.pokemon.weight}<br />
Height: {props.pokemon.height}<br />
Abilities:
<ul>
{props.pokemon.abilities.map(({ability}, index) => {
return (<li key={index}>{ability.name}</li>);
})}
</ul>
</div>
)
}
function PokemonTable (props) {
return (
<table className="pokemon-list">
<thead>
<tr>
<th>Name</th>
<th>More info.</th>
</tr>
</thead>
<tbody>
{props.children}
</tbody>
</table>
);
}
function PokemonRow (props) {
return (
<tr>
<td>{props.pokemon.name}</td>
<td>
<a href="#" onClick={() => props.onInfo(props.pokemon)}>
More info.
</a>
</td>
</tr>
);
}
class App extends React.Component {
state = {
pokemons: [],
detailedPokemons : {},
loading: false,
status : null,
previous : null,
next : null
}
componentDidMount () {
this.getPokemons(`${API}?limit=${PAGE_SIZE}`)
}
request(url) {
return fetch(url)
.then(blob => blob.json());
}
getPokemons (url) {
this.setState(state => ({
...state,
loading : true,
status : 'Fetching pokemons...'
}));
this.request(url)
.then(response => {
console.log(response)
this.setState(state => ({
...state,
previous : response.previous,
next : response.next,
pokemons : response.results,
loading : false,
status : null
}))
})
.catch(err => {
this.setState(state => ({
...state,
loading : false,
status : 'Unable to retrieved pockemons'
}));
});
}
getPokemonDetail (pokemon) {
const { detailedPokemons } = this.state;
const cachePokemon = detailedPokemons[pokemon.name];
if (cachePokemon !== undefined) { return; }
this.setState(state => ({
...state,
loading : true,
status : `Fetching ${pokemon.name} info`
}));
this.request(pokemon.url)
.then(response => {
this.setState(state => ({
...state,
loading: false,
status : null,
detailedPokemons : {
...state.detailedPokemons,
[response.name]: {
name: response.name,
abilities: response.abilities,
stats: response.stats,
weight: response.weight,
height: response.height,
base_experience: response.base_experience
}
}
}))
})
.catch(err => {
console.log(err)
this.setState(state => ({
...state,
loading : false,
status : 'Unable to retrieved pockemons'
}));
});
}
renderPokemons () {
const { pokemons } = this.state;
return pokemons.map(pokemon => (
<PokemonRow
pokemon={pokemon}
onInfo={this.handleView}
/>
));
}
renderDetailPokemons () {
const { detailedPokemons } = this.state;
return (
<ul>
{Object.keys(detailedPokemons).map(pokemonName => (
<li key={pokemonName}>
<Pokemon pokemon={detailedPokemons[pokemonName]}/>
</li>
))}
</ul>
)
}
handleView = (pokemon) => {
this.getPokemonDetail(pokemon);
}
handlePrevious = () => {
const { previous } = this.state;
this.getPokemons(previous);
}
handleNext = () => {
const { next } = this.state;
this.getPokemons(next);
}
render () {
const { loading, detailedPokemons, status, next, previous } = this.state;
return (
<div className='general'>
<div className="pokemons-info">
{ status && <Status value={status} /> }
<PokemonTable>
{this.renderPokemons()}
</PokemonTable>
<Pagination
disabled={loading}
onPrevious={this.handlePrevious}
onNext={this.handleNext}
/>
{
Object.keys(detailedPokemons).length > 0 &&
this.renderDetailPokemons()
}
</div>
</div>
)
}
}
ReactDOM.render(
<App />,
document.querySelector('#app')
);
I want to have a dynamic number of input field and also able to remove it. I can do it if this is only client side but I am getting the data from server and the issue is that I am not getting the id from the server, and that I need to be able to change the number of fields on the fly in the UI.
since I am also getting some fields from the server the manually added id was not longer in sync with the # of items.
How do i achieve this?
Here is my code
let _id = 0;
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return s4() + s4();
}
class Rules extends React.PureComponent {
constructor(props, context) {
super(props, context);
this.state = {
rules: {
rule_regulations: {}
},
numberOfInput: []
};
}
componentDidMount() {
this.props.loadRules();
}
componentWillReceiveProps(nextProps) {
if (nextProps.rules.size && nextProps.rules !== this.props.rules) {
nextProps.rules
.entrySeq()
.map(([key, value]) => {
this.setState(state => ({
rules: {
...state.rules,
rule_regulations: {
...state.rules.rule_regulations,
[key]: value
}
},
numberOfInput: [...state.numberOfInput, guid()]
}));
})
.toArray();
}
}
handleChange = e => {
const { value, name } = e.target;
this.setState({
rules: {
...this.state.rules,
rule_regulations: {
...this.state.rules.rule_regulations,
[name]: value
}
}
});
};
handleAddRules = e => {
this.setState({
numberOfInput: [...this.state.numberOfInput, guid()]
});
};
handleRemoveRules = (e, num) => {
e.preventDefault();
this.setState({
numberOfInput: this.state.numberOfInput.filter(input => input !== num)
});
};
handleSave = e => {
e.preventDefault();
const obj = {
rule_regulations: Object.values(this.state.rules.rule_regulations)
};
this.props.postRules(obj);
};
render() {
const { numberOfInput } = this.state;
return (
<div className="basic-property">
<span className="circleInputUi" onClick={this.handleAddRules}>
+
</span>
<RulesInputContainer
numberOfInput={numberOfInput}
handleChange={this.handleChange}
handleRemoveRules={this.handleRemoveRules}
handleSave={this.handleSave}
value={this.state.rules.rule_regulations}
/>
</div>
);
}
}
const RulesInputContainer = props => {
return (
<div className="rules-input">
{props.value &&
Object.keys(props.value).map(key =>
<RulesInput
key={key}
num={key}
value={props.value}
handleChange={props.handleChange}
handleRemoveRules={props.handleRemoveRules}
/>
)}
<button className="button" onClick={props.handleSave}>
Save
</button>
</div>
);
};
export default RulesInputContainer;
const RulesInput = props => {
return (
<form className="form">
<InputField
label={`Rule ${props.num + 1}`}
type="text"
name={`${props.num}`}
value={props.value[props.num] || ""}
onChange={props.handleChange}
/>
<Button onClick={e => props.handleRemoveRules(e, props.num)}>
Remove
</Button>
</form>
)
}
I want the synchronization with server data and client
You can store returned data from your server into your component state.
componentDidMount() {
this.loadRules();
}
loadRules() {
let rules = this.props.loadRules();
this.setState({rules});
}
You can keep the synchronization by setting your state with data you received from your server.
In addition, it's not related to your question. Since RulesInputContainer and RulesInput are presentational components I think you can combine those into 1 component.