Access input value in ReactDOM.render - javascript

I need to get input field's value inside a click handler inside ReactDOM.render().
The code inside App.js is
<input ref={node => {
this.todoInput = node
}} className="pull-left" />
<button onClick={this.props.addTodo}>Add Todo</button>
index.js
ReactDOM.render(<App
todos={store.getState().todos}
addTodo={() => {
console.log(this.refs);
//store.dispatch({
// type: 'ADD_TODO',
// id: nextTodoId++,
{/*text: this.refs.todoInput.value*/}
{/*});*/}
{/*this.refs.todoInput.value = '';*/}
}}
/>,
document.getElementById('root'));
Inside addTodo function , how can I access my refs object? Or ultimately to get todoInput's value
Update - One of the answers provided below works for me as an alternative solution. However, the reason what is bugging me is simple. I render App component inside of the ReactDOM.render(). Shouldn't App directly have access to it's input field as it is through some mechanism ?

In your code, you refer the todo inside addTodo function like this.refs.todoInput. Here this refers to the current instance where you are rendering ReactDOM.render.
So your input which is inside the app instance cannot be accessed in a different instance.
If you wanna access it your way then you need to add a ref to your app like this
ReactDOM.render(<App
ref={appInstance => { this.appInstance = appInstance;} }
todos={store.getState().todos}
.....
Now you can access the input inside app instance like below
addTodo={() => {
// your value from todoInput will be here..
const value = this.appInstance.todoInput.value;
}}

I suggest you to make App a stateful component and then you could pass on the ref or the value of the ref to the this.props.addTodo function.
Example code:
class App extends React.Component {
handleClick() {
this.props.addTodo(this.todoInput.value);
}
render() {
return (
<input ref={node => { this.todoInput = node }} className="pull-left" />
<button onClick={this.handleClick.bind(this)}>Add Todo</button>
)
}
}

Related

React - Transferring Data from Child to Parent

I'm building a react website and i'm stuck on a problem that has to do with passing data from a component, back to the App.js file.
so:
In my app.js file, i have a Router, that routes any request to path '/signin' to the Component. What i want to do, is i want to take a boolean from the component and store it in the app.js script, so that whenever that boolean is changed in the Component, it also changes on the app.js script.
Any suggestions on how i can do this? Any help at all would be appreciated!
the changeValue function will change the value in the App component. Since value is passed as props to ChildComponent, as soon as it changes, it will change in ChildComponent as well
const App = () => {
const [value, setValue] = useState('')
const changeValue = (value) => {
setValue(value)
}
return (
<div>
<ChildComponent
changeValue={changeValue}
value={value}
/>
</div>
)
}
const ChildComponent = ({changeValue, value}) => {
return (
<div>
<p>{value}</p>
<input
type='text'
onChange={(e) => changeValue(e.target.value)}
/>
</div>
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Store the state at the parent level, and pass down the modifier function to the child via props or context.

Calling function defined within a react function component on a click event form another function component - React.js

I am constructing some node objects in a function(prepareNodes) to pass to React Flow within a functional component A (lets say), and I have defined a custom node component(CardNode) stateless, which has a button. On button click it should trigger the function(prepareNodes) defined within Component A.
function ComponentA = ({ selectedNodes }) => {
const reactFlowWrapper = useRef(null);
const [elements, setElements] = useState([]);
const [edges, setEdges] = useState([]);
const prepareNode = async (nodeid) => {
//some service calls to fetch data and constuct nodes
setElements([ ...nodes]);
setEdges([...edges]);
}
return (
<ReactFlowProvider>
<div className="reactflow-wrapper" ref={reactFlowWrapper}>
<ReactFlow
nodes={elements}
edges={edges}
//some properties
>
</ReactFlow>
</div>
</ReactFlowProvider>
)
};
export default ComponentA;
function CardNode({ data }) {
const renderSubFlowNodes = (id) => {
console.log(id);
//prepareNode(id)
}
return (
<>
<Handle type="target" position={Position.Top} />
<div className="flex node-wrapper">
<button className="btn-transparent btn-toggle-node" href="#" onClick={() => renderSubFlowNodes(data['id']) }>
<div>
<img src={Icon}/>
</div>
</button>
</div>
<Handle type="source" position={Position.Bottom}/>
</>
);
}
export default CardNode;
I looked for some references online, and most of them suggest to move this resuable function out of the component, but since this function carries a state that it directly sets to the ReactFlow using useState hook, I dont think it would be much of a help.
Other references talks about using useCallback or useRefs and forwardRef, useImperativeHandle especially for functional component, Which I did not quite understand well.
Can someone suggest me a solution or a work around for this specific use-case of mine.
You can add an onClick handler to the each node, and within the node view you call this handler on click.
In the parent Component within the onClick handler you can call prepareNode as needed.
useEffect(() => {
setElements(
elements.map(item => {
...item,
onClick: (i) => {
console.log(i);
prepareNode();
},
})
)},
[]);
The classical approach is to have a parent object that defines prepareNode (along with the state items it uses) and pass the required pieces as props into the components that use them.
That "parent object" could be a common-ancestor component, or a Context (if the chain from the parent to the children makes it cumbersome to pass the props all the way down it).

How to pass data from vanilla JavaScript to React functional component

I'm trying to update (setState) a React functional component from within "regular" (vanilla) JavaScript.
I searched through StackOverflow but all the answers deal with passing data from React to (vanilla) JavaScript, and not the other way around.
Let's take the example from the docs:
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
To render it in JavaScript, I do:
let example = ReactDOM.render(
<Example />,
document.getElementById('example-wrapper')
);
Now suppose I want to manually update the count from the vanilla JavaScript code, outside of react. Like:
function updateExampleCount(newCount) {
example.setCount(newCount); // ???
}
I can't access the component state, as setCount is a private variable inside the function, and example returned from render is null.
If I use a class component, then render returns a reference to the component and then I can call example.setState. But I prefer not to convert my component into a class if I can avoid it.
The docs for render say:
Render a React element into the DOM in the supplied container and return a reference to the component (or returns null for stateless components).
But my component does have a state (count), it just doesn't recognize it.
If it's not possible to use the return value from render, is there another way to "get" the component and then use setCount (or some other way to set the state)?
Or do I just have to use a class component for this?
Thanks.
There is no way to access the state from outside the component. It's like trying to access a locally scoped variable from outside a function.
Using a class component wouldn't help either since you wouldn't be able to get hold of the instance of the class created inside the React app.
If you want to trigger a state change from outside the application, then the application needs to provide an event handler.
For (a really quick and dirty) example:
const outside = {
value: 2,
callbacks: [],
addCallback: function (callback) { this.callbacks.push(callback); },
setValue: function (value) {
this.value = value;
this.callbacks.forEach(
callback => callback(this.value)
);
}
};
function Component = () => {
const [val, setVal] = useState(outside.value);
useEffect(() => {
outside.addCallback((value) => setVal(value));
}, []);
return <p>{val}</p>
}
It is possible. You could pass your setCount function as a parameter to use it in your JS outside of React - but I would not really recommend this.
I would recommend that you keep your business logic and React logic separate.
The only things that need to be aware of state and will be using it are React components themselves. I would structure your code in a way that it is not coupled to React, and does not have to use or depend on React state in any way.
This is easier said than done at the beginning. If you need help with it, maybe provide a use case that you are trying to solve in this way, and a better answer might be provided.
It can be done by extending Example so it will pass a reference to the setCount function back to the parent code, see below. (This might be the same as what Oli mentioned, if so then I had the same idea and made a working implementation before answering)
const { useState } = React;
// functionFromComponent will store the function from Example.
let functionFromComponent = undefined;
const setter = (someFn) => functionFromComponent = someFn;
const Example = ({ setFunction }) => { // take `setFunction` from props
const [count, setCount] = useState(0);
setFunction(setCount); // pass setCount to the parent code
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ReactDOM.render(
<Example setFunction={setter} />,
document.getElementById('example-wrapper')
);
function buttonClicked() {
if (functionFromComponent) {
functionFromComponent(777);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="example-wrapper"></div>
<button id="regularButton" onclick="buttonClicked()">Regular button</button>

Emitting custom event in React

In Vue.js we can emit custom events along with a parameter like
this.$emit('bark', 3);
and then we can listen to this event on the parent component like
<parent-component #bark=handleBark />
handleBark (howManyTimes) {
console.log(howManyTimes);
// logs 3
}
How can we do that in React?
As #usafder, mentioned the way. I am just adding the basic callback function for an input field. So on the console you can see the current value.
Basically callback function is the way to get the data from the Child component.
Parent.js
import React from "react";
import Child from "./Child";
export default function App() {
const parentHandleChange = (e) => {
console.log(e.target.value);
};
return (
<div>
<Child handleChange={parentHandleChange} />
</div>
);
}
Child.js
import React from "react";
const Child = (props) => {
return <input onChange={props.handleChange} />;
};
export default Child;
Working codesandbox
Addition to it if you need return a custom value use like this
<Child onHandleChange={() => parentHandleChange(10)}
Because in this it won't call every-time if you want pass a value.
You just simply pass down the custom event handler as props.
For example if you have Parent and Child functional components. You can then define the custom event handler in the Parent component like:
function Parent(props) {
const handleBark = (howManyTimes) => {
console.log(howManyTimes);
};
// note below I am sending the handleBark method to Child as props
return (<Child bark={handleBark} />);
}
and then inside the Child component you can simply call it as:
props.bark(10);
You can also use this library, Evento, that I created to replicate Svelte's createEventDispatcher() and Vue's $emit.
You have to create the event emitter (named by convention evento) using the hook, and use the dispatcher as you would do with $emit by passing the name of the event and the payload :
const Dog = (props) => {
const evento = useCreateEvento(props)
return <button onCLick={() => evento('bark', 3)}>wof</button>
}
The parent Component will be able to listen to the Event as it would listen to a React Event: by using on + the capitalized name of the Component Event. The data will be stored in event.detail.
<Dog onBark={e => console.log(`barked ${e.detail} times`)} />
/* will log 'barked 3 times'*/

Modularizing code in React/Redux

The main question
I am used to using React with ES6 classes. I am also used to modularizing portions of code into separate functions. I am looking at the following example and trying to figure out how to put the value for onSubmit as a separate function.
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form
onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}
>
<input
ref={node => {
input = node
}}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
I have tried something like this:
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
function handleSubmit(e){
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => handleSubmit(e)}>
<input ref={node => {input = node }}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
But then of course it does not work as it does not recognize the input variable. I could pass the input variable to the function, but this does not seem like the right way to do it.
Question 2:
I am unfamiliar with what the following piece of code is doing:
let AddTodo = ({ dispatch }) => {
Where exactly is it getting dispatch from? Is the value of dispatch being passed into the anonymous function?
Question 3
The same with the following code:
<input ref={node => {input = node }}
Where is the value of node coming from and why is it being stored into the input variable?
Answer to Question 1
AddTodo is a React stateless functional component (SFC). It is also a function. Within the SFC is defined a variable input. In order for the handleSubmit callback to be able to make use of input, it is necessary that input be in the enclosing scope where handleSubmit is defined or input be passed as an argument to handleSubmit.
Thus, the following two implementations achieve the desired behavior:
const AddTodo = ({dispatch}) => {
let input
const handleSubmit = e => {
...
}
return (
...
onSubmit={handleSubmit}
...
)
and
const handleSubmit = (e, input) => {
...
}
const AddTodo = ({dispatch}) => {
let input
return (
...
onSubmit={e => handleSubmit(e, input)}
...
)
I highly recommend reading the following blog post by Kent Dodds, paying particular attention to the use of classes vs function closures.
Classes, Complexity, and Functional Programming
Answer to Question 2
The connect function from react-redux wraps the AddTodo component. The way in which connect is being called (with no second argument, or any arguments in this particular case) means AddTodo will receive a prop named dispatch.
To better understand how react-redux and the connect function it provides work, have a look at the documentation:
https://github.com/reactjs/react-redux/blob/master/docs/api.md
Answer to Question 3
Refs are built into React. A function passed to the ref prop receives the underlying DOM element as an argument. In this case, the function passed to the ref prop stores a reference to the DOM element in the variable input. This allows the DOM element to be accessed and mutated later by the callback passed to onSubmit (i.e. handleSubmit). See the React documentation for more details on refs:
https://reactjs.org/docs/refs-and-the-dom.html

Categories