Loop of refs for input default value - reactjs

I wan't to add a default value to dynamically added inputs. So i want to render refs by loop. I tried something like this
this.state.foods.forEach(el=> this[el.id] = React.createRef() )
but it doesn't work. Any idea to render refs dynamically? I run this loop inside of a constructor.

Instead of storing refs for a dynamic amount of inputs you can store each food as a string in an array and update a string when its corresponding input change. This way you just have to add an empty string to the array to add a new food.
Example
class App extends React.Component {
state = {
foods: ["fish", "beef", "pasta"]
};
onChange = (index, food) => {
this.setState(prevState => {
const foods = [...prevState.foods];
foods[index] = food;
return { foods };
});
};
addFood = () => {
this.setState(({ foods }) => ({ foods: [...foods, ""] }));
};
render() {
return (
<div>
{this.state.foods.map((food, index) => (
<div>
<input
key={index}
value={food}
onChange={event => this.onChange(index, event.target.value)}
/>
</div>
))}
<button onClick={this.addFood}>Add food</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Related

Push an item in array using ReactJS

class Demo extends React.Component{
constructor (){
super();
this.state = {
list : ['car','map', 'house']
}
}
inputValue(e){
var x = e.target.value;
console.log(x)
}
addValue(){
this.state.list.push();
this.setState({list: this.state.list});
}
render(){
return(
<div>
<input onChange={this.inputValue} type="text"/>
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
)
}
}
ReactDOM.render(
<Demo/>,
document.getElementById('test')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="test"></div>
Using my code, how can i push the value from <input onChange={this.inputValue} type="text"/> in list : ['car','map', 'house']. I use for this addValue function, but i can't insert the x variable from inputValue function in push() from addValue function. How to do this using my code?
You need a state value for the text-input so that your addValue() function knows what to use when its time to add a new item. The text state will be updated with anything the user types.
Working demo: https://codesandbox.io/s/magical-feynman-fze1n
import React from "react";
class Demo extends React.Component {
constructor() {
super();
this.state = {
text: "",
list: ["car", "map", "house"]
};
}
inputValue(e) {
this.setState({
text: e.target.value
});
}
addValue() {
const text = this.state.text;
this.setState({ list: [...this.state.list, text] });
}
render() {
return (
<div>
<input onChange={this.inputValue.bind(this)} type="text" />
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
);
}
}
export default Demo;
Also, refrain from doing direct state-mutations like this.state.list.push(blah). This is against React principles and can lead to unwanted visual side-effects. If you need to reference an existing state, try to create a copy of it instead. In the case for you list, we use the spread-operator to create a shallow-copy and then added the new item to the array..
Since React is all about small components and reusability consider breaking it up into two separate components... That way, if you need a form anywhere else you can reuse it...
Here is your Demo:
class Demo extends Component {
state = { list: ['car', 'map', 'house'] };
addItem = item => {
this.setState({ list: [item, ...this.state.list] });
};
render() {
return (
<div>
<Form addItem={this.addItem} />
{this.state.list.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}
}
And here is the Form:
class Form extends Component {
state = { item: '' };
handleChange = event => {
this.setState({ item: event.target.value });
};
handleSubmit = event => {
event.preventDefault();
this.props.addItem(this.state.item);
this.setState({ item: '' });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
value={this.state.item}
onChange={this.handleChange}
/>
</form>
);
}
}
Live Demo: https://stackblitz.com/edit/react-611uzp

Enumerate through a component's state in React

so I wanted to have a component iterate through an object within it's state and pass the data down to it's child. My parent component looks basically like this:
class ListContainer extends React.Component{
constructor(props){
super(props);
this.state = {"stuff": {
"pie": ["bread", "apple"],
"fries": ["potatoes", "oil"]
}
};
render(){
let rendArr = [];
for(recipe in this.state.stuff){
let newRecipe = <Child tableName={recipe} recipeData={this.state.stuff[recipe]} />;
rendArr.push(newRecipe);
}
return(
<div id="11"> I work
{rendArr}
</div>
);
}
However, I get an error saying that the "recipe" placeholder I used in the for loop isn't defined. I'm guessing I'm using the for loop here wrong with JSX, but I don't know the right way to iterate through an object. I know I could probably just convert it into an array of objects or something, but right now I'd like to understand why this for loop doesn't work in React.
In ReactJS: typical practice is to render lists using Array.prototype.map().
Object.entries() and Destructuring Assignment can be combined to reach a convenient form.
See below for a practical example.
// List.
class List extends React.Component {
// State.
state = {
recipes: {
pie: ['bread', 'apple'],
fries: ['potatoes', 'oil']
}
}
// Render.
render = () => (
<div id="11">
<h1>Map</h1>
{this.map()}
<h1>For Loop</h1>
{this.forLoop()}
</div>
)
// Map.
map = () => Object.entries(this.state.recipes).map(([name, recipe]) => <Recipe key={name} name={name} recipe={recipe}/>)
// For Loop.
forLoop = () => {
const list = []
for (let name in this.state.recipes) {
const recipe = this.state.recipes[name]
const line = <Recipe key={name} name={name} recipe={recipe}/>
list.push(line)
}
return list
}
}
// Recipe.
const Recipe = ({name, recipe}) => (
<div>
<h3 style={{textTransform: 'capitalize'}}>{name}</h3>
{recipe.map(ingredient => <div>{ingredient}</div>)}
</div>
)
ReactDOM.render(<List/>, document.querySelector('#root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Instead of pushing component in an array, push the object and pull that in the render using map method:
render(){
let rendArr = [];
for(recipe in this.state.stuff){
rendArr.push({tn: recipe, rd: this.state.stuff[recipe]});
}
return(
<div id="11"> I work
{
rendArr.map(el=> {
<Child tableName={el.tn} recipeData={el.rd} />
})
}
</div>
);
}
use map instead of for..in loop:
render(){
const rendArr = this.state.stuff.map((recipe, i) => <Child
key={i}
tableName={recipe}
recipeData={recipe}
/>);
return(
<div id="11"> I work
{rendArr}
</div>
);
}

How to manipulate style/atributes in React DOM

I have two components. If I hovered over one I'd like to move (by changing style proporties) the other one component.
How can I achieve that?
In pure js it's simply
let elem1 = document.querySelector('.elem1');
let elem2 = document.querySelector('.elem2');
elemt1.addEventListener('mouseover', () => {
elem2.style.right = "200px" //or any other style property
})
So.. in react we can use "ref" and this works if I define static ref
import React, {Component} from 'react';
class MainCanvas extends Component {
onHover(){
console.log(this.refs.mybtntest);
}
render(){
return(
<div>
<h1 onMouseEnter={() => this.onHover()}> Testing</h1>
<button ref="mybtntest">Close</button>
</div>
);
}
}
export default MainCanvas
However in my case I need to each component should has dynamically added "ref" atribute.. so my code looks like below
import React, {Component} from 'react';
class Test extends Component {
onHover(e, dynamicRef){
console.log(dynamicRef); //correct number of ref
console.log(this.refs.dynamicRef); //undefined
console.log(this.refBtnName); //button reference but eachtime is overrided
console.log(this.dynamicRef);//undefinded
}
render(){
const elements = this.props.elements.map( element => {
let refBtnName = element.id + "btn";
return [
<ComponentElement
onHover={(e) => this.onHover(e, refBtnName)}
key={element.id} {...element}
/>,
<button key={element.id*2}
ref={refBtnName => this.refBtnName = refBtnName} //each time he will be overrided :(
className={`${refBtnName} deleteComponentBtn`} >
Close
</button>
]
})
return(
<div>
{elements}
</div>
);
}
}
export default Test
A real goal is that I want to positioning the button relative to the element. I could use div for this purpose as a wrapper but I don't want it.
So I thought to use for example this piece of code
onHover(e, dynamicRef){
Math.trunc(e.target.getBoundingClientRect().right)
dynamicRef.style.right = `${right}px`;
}
If you need dynamic object keys you shouldn't use the dot . and instead use the brackets:
ref={ref=> this[refBtnName] = ref}
Note that i changed the inline parameter to ref instead of refBtnName so you won't get variable names conflicts.
Running example:
class App extends React.Component {
state = {
items: [
{ name: 'John', id: 1 },
{ name: 'Mike', id: 2 },
{ name: 'Jane', id: 3 },
]
}
move = refName => e => {
this[refName].style.right = '-90px';
}
render() {
const { items } = this.state;
return (
<div>
{
items.map(item => {
return (
<div key={item.id} >
<div
ref={ref => { this[item.name] = ref }}
style={{ position: 'relative' }}
>
{item.name}
</div>
<button onClick={this.move(item.name)}>Move {item.name}</button>
</div>
)
})
}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Rxjs debounce on react text input component

I have the following react component
<input className={styles.incSrchTextBox} type="text" name="search" placeholder="Search.."
onChange={this.onChange} />
onChange(e) {
const newText = e.target.value;
console.log(newText);
this.setState({ searchText: newText });
}
How do I use debounce on rxjs on this?
You will need to cretae observable from change events(for example using Subject) and then debounce on that.
Here is the fully featured example for you:
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
search: '',
debounced: '',
};
this.onSearch$ = new Rx.Subject();
this.onSearch = this.onSearch.bind(this);
}
componentDidMount(){
this.subscription = this.onSearch$
.debounceTime(300)
.subscribe(debounced => this.setState({ debounced }));
}
componentWillUnmount() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
onSearch(e) {
const search = e.target.value;
this.setState({ search });
this.onSearch$.next(search);
}
render() {
const { search, debounced } = this.state;
return (
<div>
<input type="text" value={search} onChange={this.onSearch} />
<div>debounced value: {debounced}</div>
</div>
);
}
}
ReactDOM.render(
<Search />,
document.getElementById('root')
);
<script src="https://unpkg.com/rxjs#5.4.0/bundles/Rx.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
This would be a good use case for Refract!
The first step would be to pull the input out into a separate component:
const Input = ({ onChange, value }) => (
<input type="text" value={value} onChange={onChange} />
)
Next step would be to wrap this component with Refract's withEffects higher-order component, with a handler and an aperture to handle the side-effects like this:
import { withEffects } from 'refract-rxjs'
import { debounceTime } from 'rxjs/operators'
const Input = ({ onChange, value }) => (
<input type="text" value={value} onChange={onChange} />
)
const aperture = () => component =>
component.observe('value').pipe(debounceTime(300))
const handler = ({ onUpdate }) => value => onUpdate(value)
const DebouncedInput = withEffects(handler)(aperture)(Input)
An aperture lets you observe your component's props. In this case, it would make sense to observe the value prop - every time the value changes, the component.observe('value') stream gets a new value.
The handler is a function called with each value output by the aperture's stream. In this case, the debounced value is passed straight through to a new prop called onUpdate.
Both apertures and handlers are explained in detail in the docs - Observing React introduces apertures, and Handling Effects explains handlers.
As an example of how you would use this:
class Search extends React.Component {
state = { debounced: '', search: '' }
onSearch = e => this.setState({ search: e.target.value })
onUpdate = debounced => this.setState({ debounced })
render() {
return (
<div>
<DebouncedInput
type="text"
value={this.state.search}
onChange={this.onSearch}
onUpdate={this.onUpdate}
/>
<div>debounced value: {debounced}</div>
</div>
)
}
}
With this code, the text DebouncedInput would display the user's input instantly (which is ideal for UX), while debouncing the side-effect of calling the onUpdate callback. It would then be trivial to expose this onUpdate to components which consume the Search component!
I agree with the example by Oles Savluk. In addition, I would extract the Subject logic out of the component. It doesn't need to live inside the component, as it has no state, and I think this also makes the component easier to understand.
Also: The example is updated to use RxJS 6.2.2
const { Subject } = rxjs;
const { debounceTime } = rxjs.operators;
const onSearch$ = new rxjs.Subject().pipe(
debounceTime(300)
);
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
search: '',
debounced: '',
};
}
componentDidMount(){
this.subscription = onSearch$.subscribe(
debounced => this.setState({ debounced })
);
}
componentWillUnmount() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
onSearch = (e) => {
const search = e.target.value;
this.setState({ search });
onSearch$.next(search);
}
render() {
const { search, debounced } = this.state;
return (
<div>
<input type="text" value={search} onChange={this.onSearch} />
<div>debounced value: {debounced}</div>
</div>
);
}
}
ReactDOM.render(
<Search />,
document.getElementById('root')
);
<script src="https://unpkg.com/rxjs#6.2.2/bundles/rxjs.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

React ref syntax and components as pure functions

I have the following TodoApp written in React:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
<script src="https://unpkg.com/react#15.3.2/dist/react.js"></script>
<script src="https://unpkg.com/react-dom#15.3.2/dist/react-dom.js"></script>
<title>React! React! React!</title>
</head>
<body>
<div class="container">
<div id="container" class="col-md-8 col-md-offset-2"> </div>
</div>
<script type="text/babel">
console.clear();
const Title = () => {
return (
<div>
<div>
<h1>to-do</h1>
</div>
</div>
);
}
const TodoForm = ({addTodo}) => {
// Input Tracker
let input;
// Return JSX
return (
<div>
<input ref={node => {
input = node;
}} />
<button onClick={() => {
addTodo(input.value);
input.value = '';
}}>
+
</button>
</div>
);
};
const Todo = ({todo, remove}) => {
// Each Todo
return (<li onClick={() => {remove(todo.id)}}>{todo.text}</li>);
}
const TodoList = ({todos, remove}) => {
// Map through the todos
const todoNode = todos.map((todo) => {
return (<Todo todo={todo} key={todo.id} remove={remove}/>)
});
return (<ul>{todoNode}</ul>);
}
// Contaner Component
// Todo Id
window.id = 0;
class TodoApp extends React.Component{
constructor(props){
// Pass props to parent class
super(props);
// Set initial state
this.state = {
data: []
}
}
// Add todo handler
addTodo(val){
// Assemble data
const todo = {text: val, id: window.id++}
// Update data
this.state.data.push(todo);
// Update state
this.setState({data: this.state.data});
}
// Handle remove
handleRemove(id){
// Filter all todos except the one to be removed
const remainder = this.state.data.filter((todo) => {
if(todo.id !== id) return todo;
});
// Update state with filter
this.setState({data: remainder});
}
render(){
// Render JSX
return (
<div>
<Title />
<TodoForm addTodo={this.addTodo.bind(this)}/>
<TodoList
todos={this.state.data}
remove={this.handleRemove.bind(this)}
/>
</div>
);
}
}
ReactDOM.render(<TodoApp />, document.getElementById('container'));
</script>
</body>
</html>
Questions:
What is this syntax:
const TodoForm = ({addTodo}) => {
// Input Tracker
let input;
// Return JSX
return (
<div>
<input ref={node => {
input = node;
}} />
I think I get what ref is but what is that node just inside the curly braces? If it's a function declaration, where are the parenthesis around node? What is going on?
Also, at the end, we render the TodoApp which renders TodoForm like this:
<TodoForm addTodo={this.addTodo.bind(this)}/>
Does that just pass addTodo to the functionally declared component, not as props, but merely an argument?
const TodoForm = ({addTodo}) => {
Is this correct? addTodo comes in merely as an argument and not as props?
So in the following function
const TodoForm = ({addTodo}) => {
// Input Tracker
let input;
// Return JSX
return (
<div>
<input ref={node => {
input = node;
}} />
<button onClick={() => {
addTodo(input.value);
input.value = '';
}}>
+
</button>
</div>
);
};
The first line is an example of destructuring in ES6 What happens is that in const TodoForm = ({addTodo}) => { props gets passes to the TodoForm Component which is stateless and in the props you have addTodo as a prop so out of all the props we are extracting addTodo
Also for refs a callback approach is being followed. It is an ES6 style to write a function. Here node is an argument and it doesn't contain any parenthesis because it is a single argument and ES6 gives you flexibility to omit the parenthesis. Also inside the {} you have the body of the function
In your code node refers to the DOM element and you are assigning its reference to the variable input that you have defined. Now you can refer the DOM with input rather than assigning ref as <input ref="myValue"/> and then refering it as this.refs.myValue.
I hope was able to explain it properly.
Read the following documentation on React ref callback approach for a detailed explaination.
let's say we have this one :
const TodoForm = (data) => {
const addTodo = data.addTodo //can be const myFunc = data.addTodo
// Input Tracker
...
as an enhancement we can do it like that :
const TodoForm = (data) => {
const {addTodo} = data //can be const {addTodo as myFunc} = data
// Input Tracker
...
once more !
as an enhancement we can do it like that :
//notice that we moved the {addTodo} directly to replace data
const TodoForm = ({addTodo}) => {
//can be ({addTodo: myFunc}) => {
// Input Tracker
...

Resources