Modularizing code in React/Redux - javascript

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

Related

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).

Can someone explain me how custom hooks get the data and in depth flow of custom hooks

//use Input HOOK
I want to know that how this custom hook work
import { useState } from "react";
export default initialValue => {
const [value, setValue] = useState(initialValue);
return {
value,
onChange: event => {
setValue(event.target.value);
},
reset: () => setValue("")
};
};
//todo form
How this onchange method work how it update the data even though no onchange function is write in this programm
import React from "react";
import TextField from "#material-ui/core/TextField";
import useInputState from "./useInputState";
const TodoForm = ({ saveTodo }) => {
const { value, reset, onChange } = useInputState("");
return (
<form
onSubmit={event => {
event.preventDefault();
saveTodo(value);
reset();
}}
>
<TextField
variant="outlined"
placeholder="Add todo"
margin="normal"
value={value}
onChange={onChange}
/>
</form>
);
};
export default TodoForm;
view full programm Code Sandbox link
Functions in JS are treated like any other variable. So the de-structured onChange (in the component) is taking the reference for a function which is defined anonymously in the custom hook, which is then used by the onChange method of the TextField component.
This is similar to how you pass variables by reference is JS.

How can I pass props from one component to another withour Redux

I have this kind of jsx and I want to pass number value from SendNumberPage to CheckNumberPage.
App.js
<EditNumberPage/>
<br/>
<SendNumberPage/>
<br/>
<CheckNumberPage/>
SendNumberPage.js
function onChangeHandler(event) {
setState({
...state,
number: event.target.value
})
}
I tried using React.createContext but it didn't work for me. Please Help
sendNumberPage.js
const [state, setState] = useState(
{
number: '+99979787'
}
)
const NumberContext = React.createContext()
return (
<NumberContext.Provider value={state.number}>
<div>
....
....
</div>
</NumberContext.Provider>
)
checkNumberPage.js
const CheckNumberPage = () => {
const value = useContext(NumberContext)
console.log(value)
return (
.......
)
}
Console says Attempted import error: 'NumberContext' is not exported from './SendNumberPage'.
Depending on how complex your app is you may want to do this in different ways.
Using react context api is a good way to do it, and it is scalable and suitable for all app sizes.
You should check out the react tutorials for that.
If your app is very small (1 layer) and you just want a 'quick fix' you could pass a change listener callback to one component and update the props in the other component.
<EditNumberPage/>
<br/>
<SendNumberPage onChange={(n) => {setNumber(n)} />
<br/>
<CheckNumberPage number={number}/>

What is correct way to fix this 'Invalid Hook Call' error in react app?

Well, i have this error
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
I tried alot of different options to fix this, but i failed.
Here is my code
export const DataInput = () => {
const Post = (testTitle, testText) => {
useFirestore().collection('schedule-data').doc('test').set({
testTitle: testTitle,
testText: testText
})
}
return(
<Button
variant="primary"
onClick={()=> Post(testTitle, testText)}>
POST data
</Button>
Deleted some of code that does not matter
Hooks can only be called while rendering a component, so they need to be in the body of your component.
export const DataInput = () => {
const firestore = useFirestore();
const Post = (testTitle, testText) => {
firestore.collection('schedule-data').doc('test').set({
testTitle: testTitle,
testText: testText
})
}
// etc
}
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls. (If you’re curious, explanation available here)
According to you code samle I may suggest that testTitle, testText available in DataInput in some way, thus you may create onClick handler with useCallback. React will create callback for use as handler, and re-create only when testTitle, testText changed.
import {useCallback} from 'react';
export const DataInput = () => {
const makePost = useCallback(() => {
useFirestore().collection('schedule-data').doc('test').set({
testTitle: testTitle,
testText: testText
})
}, [testTitle, testText]);
return (
<Button
variant="primary"
onClick={makePost}
{/* Avoid inline callback declaration */}
>
POST data
</Button>
)
}

How to pass a props to event handler

I would like to pass a custom props to the onClick handler.
I cannot return an inline function as below, because I will have to later fire redux action creator as part of the handler (async action is not allowed)
onClick={()=>this.handleClick("v")}
Using middleware seems an overkill to me..
For simplicity purpose, please just ignore the redux part. Just say we can't use inline arrow function like this.
The example code below is just a POC approach that I borrow from input component, where value is an inherited props.
I am OK with any props("custom" as I said)
class Test extends React.Component {
handleClick = (event) => {
console.log(event.target.value);
}
render() {
return (
<div>
<label
value="v"
onClick={this.handleClick}
>
TEST Label
</label>
</div>
)
}
I expect console log to output a custom value -- "v"
your options are really limited. You should either use the inline arrow function and handle you async whatever problem in some other way or you should find a way to keep your state updated with current value of your label value. If it was an input onChange = {this.handleChange} would do it. this way your code will look like this:
handleClick(){
const {value} = this.state;
doSomething(value)
}
updateValue(input){
/* this.setState({
value : input
})*/
//in your case :
this.setState({
value : 'v'
})
}
render(){
return(
<label
value= {this.state.value}
onClick={this.handleClick}
>Text</label>
)
}
hope this helps
use mapDispatchToProps to pass action to the components and call it similar to above.
import React from "react";
import { action1 } from "./actions";
const App = ({action1})=> (
<button onClick={()=>action1("one")}>Click Me</button>
);
export default connect(null, {action1})(App);

Categories