How can I fix the TextInput issue? in react-native? - javascript

If i write a text korean in ingreInput and use space twice, it will automatically create a dot
like this 안.
However, this happens only when i use Korean.
what should i do ? if i want to resolve this problem?
this is my code
(Upload.js)
const IngreInput = styled.TextInput`
width: 150px;
height: 50px;
border:1px solid #A9A9A9;
margin:5px;
border-radius: 50px;
color: #292929;
text-align: center;
`;
const Upload = ({}) => {
const [ingre1, onChangeIngre1, setIngre1] = useInput('');
}
return (
<IngreInput
placeholder="ingre1"
value={ingre1}
onChange={onChangeIngre1}
/>
)
(useInput.js)
export default (initValue = null) => {
const [value, setValue] = useState(initValue);
const handler = useCallback((e) => {
setValue(e.nativeEvent.text);
}, []);
return [value, handler, setValue];
};

Related

setTimeout runs indefinetly inside useEffect

I am trying to make a notification toast component. And I want it to be removed after 2 seconds (not-shown on the screed) Although, it is removed (not-shown on the screen via top:-100 argument), the component is getting rendered infinitely. You can see it from the console.log's I have placed inside the component and inside the useEffect call with setTimeout.
My expectation is that setTimeout should run setShowState after 2 seconds and then useEffect should do the cleanup and remove the timer. So everything is back to normal until showState changes.
import React, {useEffect, useState} from 'react'
import I18n from '../../i18n'
import styled from 'styled-components'
import {createGlobalStyle} from 'styled-components'
import {useSelector} from 'react-redux'
const NotificationStyle = createGlobalStyle`
#media (max-width: 500px) {
.notification_mssg {
left: 10px;
}
}
`
const Container = styled.div`
color: white;
position: fixed;
top: ${(props) => props.top}px;
right: 16px;
z-index: 2000;
transition: top 0.5s ease;
`
const NoticitactionIcon = styled.div`
float: left;
font-size: 27px;
width: 40px;
height: 40px;
text-align: center;
`
const NotificationMessage = styled.span`
padding: 10px;
line-height: 40px;
`
function NotificationAlertRoot(props) {
const create_notification = useSelector((state) => state.notifications.create_notification)
const {message, code} = create_notification.success_info
const [showState, setShowState] = useState({top: -100, msg: message, bgColor: '#444'})
// show notification
useEffect(() => {
setShowState({top: 96, msg: I18n.t(message), bgColor: backgroundColor(code)})
}, [message, code])
console.log('amIrendered', showState) // although showState doesn't change, component is getting rendered infinitely :/
// hide notification after 2 seconds
useEffect(() => {
const timerId = setTimeout(() => {
setShowState({
top: -100,
msg: '',
bgColor: `#ffffff00`,
})
console.log("timerId", timerId) // I see timerId is changing so the problem most probably in this useEffect call.
}, 2000)
return () => {
clearTimeout(timerId)
}
}, [showState])
const notificationIcon = (bgColor) => {
switch (bgColor) {
case '#32c786':
return (
<NoticitactionIcon style={{background: '#2aa872'}}>
<i className="zmdi zmdi-info" />
</NoticitactionIcon>
)
case '#ffc721':
return (
<NoticitactionIcon style={{background: '#fabb00'}}>
<i className="zmdi zmdi-alert-triangle" />
</NoticitactionIcon>
)
case '#ff6b68':
return (
<NoticitactionIcon style={{background: '#ff4642'}}>
<i className="zmdi zmdi-alert-circle" />
</NoticitactionIcon>
)
default:
return <span></span>
}
}
function backgroundColor(code) {
switch (Math.floor(code / 100)) {
case 2:
return '#32c786'
case 3:
return '#ffc721'
case 4:
return '#ff6b68'
case 5:
return '#ff6b68'
default:
return '#444'
}
}
return (
<React.Fragment>
<NotificationStyle />
<Container
className="notification_mssg"
top={showState.top}
style={{background: showState.bgColor}}
>
{notificationIcon(showState.bgColor)}
<NotificationMessage>{showState.msg}</NotificationMessage>
</Container>
</React.Fragment>
)
}
export default NotificationAlertRoot
Do you have an idea what is wrong above?
I guess the problem comes from your dependency array. Your useEffect is dependent on showState and each time, you are calling setShowState in your useEffect when setShowState is called showState changes and then again, your useEffect
gets invoked(it is dependent on ShowState), and again setShowState is called and ...
infinity loop!
I found the root of the problem. Sometimes you are too focused on something and you forget the little details of useEffect. It is always dangerous to provide objects as dependency arrays to useEffect. The dependency array values should be simple values. So now I introduced a new state (flag, setFlag) with just boolean values and I make the second useEffect just to follow that simple value. Everything is working just fine now.
import React, {useEffect, useState} from 'react'
import I18n from '../../i18n'
import styled from 'styled-components'
import {createGlobalStyle} from 'styled-components'
import {useSelector} from 'react-redux'
const NotificationStyle = createGlobalStyle`
#media (max-width: 500px) {
.notification_mssg {
left: 10px;
}
}
`
const Container = styled.div`
color: white;
position: fixed;
top: ${(props) => props.top}px;
right: 16px;
z-index: 2000;
transition: top 0.5s ease;
`
const NotificationIcon = styled.div`
float: left;
font-size: 27px;
width: 40px;
height: 40px;
text-align: center;
`
const NotificationMessage = styled.span`
padding: 10px;
line-height: 40px;
`
function NotificationAlertRoot(props) {
const create_notification = useSelector((state) => state.notifications.create_notification)
const {message, code} = create_notification.success_info
const [showState, setShowState] = useState({top: -100, msg: message, bgColor: '#444'})
const [flag, setFlag] = useState(false) // when you follow the showState at the second useEffect you have an infinite loop. Because it is an object.
// show notification
useEffect(() => {
setShowState({top: 96, msg: I18n.t(message), bgColor: backgroundColor(code)})
setFlag(true)
}, [message, code])
// hide notification after 2 seconds
useEffect(() => {
const timerId = setTimeout(() => {
setShowState({top: -100,msg: '', bgColor: `#ffffff00`})
setFlag(false)
}, 2000)
return () => {
clearTimeout(timerId)
}
}, [flag]) // showState
const notificationIcon = (bgColor) => {
switch (bgColor) {
case '#32c786':
return (
<NotificationIcon style={{background: '#2aa872'}}>
<i className="zmdi zmdi-info" />
</NotificationIcon>
)
case '#ffc721':
return (
<NotificationIcon style={{background: '#fabb00'}}>
<i className="zmdi zmdi-alert-triangle" />
</NotificationIcon>
)
case '#ff6b68':
return (
<NotificationIcon style={{background: '#ff4642'}}>
<i className="zmdi zmdi-alert-circle" />
</NotificationIcon>
)
default:
return <span></span>
}
}
const backgroundColor = (code) => {
switch (Math.floor(code / 100)) {
case 2:
return '#32c786'
case 3:
return '#ffc721'
case 4:
return '#ff6b68'
case 5:
return '#ff6b68'
default:
return '#444'
}
}
return (
<React.Fragment>
<NotificationStyle />
<Container
className="notification_mssg"
top={showState.top}
style={{background: showState.bgColor}}
>
{notificationIcon(showState.bgColor)}
<NotificationMessage>{showState.msg}</NotificationMessage>
</Container>
</React.Fragment>
)
}
export default NotificationAlertRoot

React: adding ternary operator for component breaks progress bar

I am still learning React and am trying to parse text from an API call into json objects while displaying progress in a progress bar.
Below, Home.js uses useEffect hook to
call getData that grabs (lots of) API text data
call parseText to parse returned text
call parseSeriesId to parse returned results
set seriesIdParts
Finally, seriesIdParts is passed to Occupation.js as props. That works fine and working code is displayed below.
My problem is that I want to make the progress bar disappear when parseText is finished. I found a post that did it using a ternary operator, so trying that I changed this:
<ProgressBar percentage={percentage}/>
to this:
{percentage >= 100 ? console.log("Done.") : <ProgressBar percentage={percentage}/>
and now the progressBar disappears, but it no longer populates green. I can log the percentage value just fine in both ProgressBar.js and Filler.js, so it seems to be that the Filler width element is not picking up the percentage value increment:
<div className="filler" style={{ width: `${percentage}%`}}>
How do I fix this?
App.js
import React from 'react';
import './App.css';
import Home from './components/Home';
function App() {
return (
<div className="App">
<Home />
</div>
);
}
export default App;
Home.js
import React from 'react';
import { useState, useEffect } from 'react';
import Occupation from './Occupation';
import ProgressBar from './ProgressBar';
const Home = () => {
const [seriesIdParts, setSeriesIdParts] = useState([]);
const [percentage, setPercentage] = useState(0);
async function getData(url) {
const response = await fetch(url);
if( response.status !== 200 ) {
throw new Error('Problem calling ' + url);
}
return response.text();
}
function parseSeriesId(data) {
let results = new Set()
data.forEach(function(value) {
let areaCode = value.seriesId.substring(4, 11);
let industry = value.seriesId.substring(11, 17);
let occupation = value.seriesId.substring(17, 23);
let dataType = value.seriesId.substring(23, value.seriesId.length);
let seriesIdVal = value.value;
if( dataType === '04') {
let result = {areaCode: areaCode, industry: industry, occupation: occupation, dataType: dataType, seriesIdVal: seriesIdVal};
results.add(result);
}
})
return results;
}
useEffect(() => {
getData('https://download.bls.gov/pub/time.series/oe/oe.data.0.Current')
.then(data => {
const parseText = function(data) {
data = data.split('\n');
let localNum = 1090;
let results = new Set();
let intervalAmount = 100 / localNum;
for( let i=1; i<=localNum; i++ ) {
setPercentage((i * intervalAmount));
let line = data[i];
if( line !== "") {
line = line.split('\t');
let seriesIdValue = line[0].trim();
let valueValue = line[3].trim();
if (valueValue !== '-') {
results.add({'seriesId': seriesIdValue, 'value': valueValue});
}
}
}
return results;
}
return parseText(data, 100);
})
.then(data => {
return parseSeriesId(data);
})
.then(data => {
setSeriesIdParts(data);
})
.catch(err => console.log(err));
}, [percentage]);
return(
<div className="homeComponent">
<label id="parsetext" >
Parsing Text...
<ProgressBar percentage={percentage}/>
</label>
{seriesIdParts && <Occupation seriesIdParts={seriesIdParts}/>}
</div>
);
}
export default Home;
ProgressBar.js
import React, { useState } from 'react';
import Filler from './Filler';
const ProgressBar = ({percentage}) => {
// console.log('Pbar: ', percentage); <== Displays fine!
return (
<div className="progressbar">
<Filler percentage={percentage}/>
</div>
);
}
export default ProgressBar;
Filler.js
import React from 'react';
const Filler = ({percentage}) => {
return (
<div className="filler" style={{ width: `${percentage}%`}}>
{console.log('filler: ' + percentage)}. <== Displays fine!
</div>
);
}
export default Filler;
Occupation.js
import React from 'react';
import { useState, useEffect } from 'react';
const Occupation = ({seriesIdParts}) => {
console.log('seriesIdParts in Occupation:', seriesIdParts.size) <== Displays fine!
//other code...
};
export default Occupation;
App.css
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
#media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
#keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.progressbar {
margin: 4em auto;
width: 80%;
height: 2em;
background-color: lightgray;
border-radius: 30px;
}
.filler {
height: inherit;
width: 0px;
background-color: green;
border-radius: inherit;
}
#parsetext {
text-align: 'center';
margin: 4em 0 0;
}
UPDATE: If there is a better way to make the progress bar disappear, I'm open to suggestions!
What if you set the height of the filler class to like 20px instead of inherit just to try?
.filler {
height: inherit;
width: 0px;
background-color: green;
border-radius: inherit;
}
I tried to test just that div and I couldn't see it with "inherit"

How do I fetch search results from multiple API using reactjs

I am new to ReactJS and I am trying to create a search feature with react by fetching data from multiple API. Below is the Search.js file. I tried so many times to make it functions and make the results appear live while typing. I however keep on getting this error message TypeError: values.map is not a function. Where am I going wrong and how do I fix it?
function Search() {
const [input, setInput] = useState("");
const [results, setResults] = useState([]);
const urls = [
'https://jsonplaceholder.typicode.com/posts/1/comments',
'https://jsonplaceholder.typicode.com/comments?postId=1'
]
Promise.all(urls.map(url => fetch(url)
.then((values) => Promise.all(values.map(value => value.json())))
.then((response) => response.json())
.then((data) => setResults(data))
.catch(error => console.log('There was a problem!', error))
), [])
const handleChange = (e) => {
e.preventDefault();
setInput(e.target.value);
}
if (input.length > 0) {
results.filter((i) => {
return i.name.toLowerCase().match(input);
})
}
return ( <
div className = "search"
htmlFor = "search-input" >
<
input type = "text"
name = "query"
value = {
input
}
id = "search"
placeholder = "Search"
onChange = {
handleChange
}
/> {
results.map((result, index) => {
return ( <
div className = "results"
key = {
index
} >
<
h2 > {
result.name
} < /h2> <
p > {
result.body
} < /p> {
result
} <
/div>
)
})
} </div>
)
}
.search {
position: relative;
left: 12.78%;
right: 26.67%;
top: 3%;
bottom: 92.97%;
}
.search input {
/* position: absolute; */
width: 40%;
height: 43px;
right: 384px;
margin-top: 50px;
top: 1.56%;
bottom: 92.97%;
background: rgba(0, 31, 51, 0.02);
border: 1px solid black;
border-radius: 50px;
float: left;
outline: none;
font-family: 'Montserrat', sans-serif;
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 20px;
/* identical to box height */
display: flex;
align-items: center;
/* Dark */
color: #001F33;
}
/* Search Icon */
input#search {
background-repeat: no-repeat;
text-indent: 50px;
background-size: 18px;
background-position: 30px 15px;
}
input#search:focus {
background-image: none;
text-indent: 0px
}
<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>
Issue
The response you get from fetch(url) is just the one single response, so there's nothing to map.
The data fetching occurs in the function body of the component, so when working this causes render looping since each render cycle fetches data and updates state.
The input.length > 0 filtering before the return does nothing since the returned filtered array isn't saved, and it also incorrectly searches for sub-strings.
Attempt to render result object in the render function, objects are invalid JSX
Solution
Skip the .then((values) => Promise.all(values.map((value) => value.json()))) step and just move on to accessing the JSON data.
Move the data fetching into a mounting useEffect hook so it's run only once.
Move the filter function inline in the render function and use string.prototype.includes to search.
Based on other properties rendered and what is left on the result object I'll assume you probably wanted to render the email property.
Code:
function Search() {
const [input, setInput] = useState("");
const [results, setResults] = useState([]);
useEffect(() => {
const urls = [
"https://jsonplaceholder.typicode.com/posts/1/comments",
"https://jsonplaceholder.typicode.com/comments?postId=1"
];
Promise.all(
urls.map((url) =>
fetch(url)
.then((response) => response.json())
.then((data) => setResults(data))
.catch((error) => console.log("There was a problem!", error))
),
[]
);
}, []);
const handleChange = (e) => {
e.preventDefault();
setInput(e.target.value.toLowerCase());
};
return (
<div className="search" htmlFor="search-input">
<input
type="text"
name="query"
value={input}
id="search"
placeholder="Search"
onChange={handleChange}
/>
{results
.filter((i) => i.name.toLowerCase().includes(input))
.map((result, index) => {
return (
<div className="results" key={index}>
<h2>{result.name}</h2>
<p>{result.body}</p>
{result.email}
</div>
);
})}
</div>
);
}

React useState lagging by one keystroke

Okay - I'm having an issue with my react app onChange attribute lagging by one keystroke.
I am pretty sure this has to do with this code block running before the state is being updated.
if (hex.length === 3) {
let newColor = hex.join('');
let newColors = [...colors, newColor];
setColors(newColors);
setHex([]);
}
I tried moving the above block to a useEffect hook (as such) so that it would run when the value of hex changes to remedy this.
useEffect(() => {
if (hex.length === 3) {
let newColor = hex.join('');
let newColors = [...colors, newColor];
setColors(newColors);
setHex([]);
}
}, hex)
This did not work as expected, and I'm still facing the same issue. The goal is when an input is received, if the length of the total ASCII input is 3 characters or more, it will convert that text ([61, 61, 61] for example) into hex string of 6 characters, which will eventually be converted into a color hex code.
All of my code is as follows.
import TextInput from './components/TextInput';
import Swatch from './components/Swatch';
import './App.css';
function App() {
const [colors, setColors] = useState([]);
const [hex, setHex] = useState([]);
const [text, setText] = useState();
const convertToHex = (e) => {
const inputText = e.target.value;
setText(inputText);
for (let n = 0, l = inputText.length; n < l; n++) {
let newHex = Number(inputText.charCodeAt(n)).toString(16);
let newHexArr = [...hex, newHex];
setHex(newHexArr);
}
if (hex.length === 3) {
let newColor = hex.join('');
let newColors = [...colors, newColor];
setColors(newColors);
setHex([]);
}
};
return (
<div className='App'>
<h1 id='title'>Color Palette Generator</h1>
<TextInput func={convertToHex} />
<Swatch color='#55444' />
</div>
);
}
export default App;
How is the TextInput component managing its state (the input's value)?
I suspect you might have a problem there, as changing that TextInput component with a simple controlled input:
<input type="text" onChange={ handleOnChange } value={ text } />
And using your existing code as handleOnChange works as expected:
const App = () => {
const [colors, setColors] = React.useState([]);
const [hex, setHex] = React.useState([]);
const [text, setText] = React.useState('');
const handleOnChange = (e) => {
const inputText = e.target.value;
// I thought your problem was related to `text`...:
setText(inputText);
for (let n = 0, l = inputText.length; n < l; n++) {
let newHex = Number(inputText.charCodeAt(n)).toString(16);
let newHexArr = [...hex, newHex];
setHex(newHexArr);
}
if (hex.length === 3) {
// But looking at the comments it looks like it's related to `colors`, so as
// pointed out already you should be using the functional version of `setState`
// to make sure you are using the most recent value of `colors` when updating
// them here:
setColors(prevColors => [...prevColors, hex.join('')]);
setHex([]);
}
};
return (
<div className="app">
<h1 className="title">Color Palette Generator</h1>
<input className="input" type="text" onChange={ handleOnChange } value={ text } />
<pre>{ hex.join('') }</pre>
<pre>{ colors.join(', ') }</pre>
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#app'));
body {
font-family: monospace;
margin: 0;
}
.app {
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.title {
display: flex;
margin: 32px 0;
}
.input {
margin: 0 4px;
padding: 8px;
border: 2px solid black;
background: transparent;
border-radius: 2px;
}
.as-console-wrapper {
max-height: 45px !important;
}
<script src="https://unpkg.com/react#16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
In any case, there are a few other things that could be improved on that code, such as using a single useState, using useCallback, adding a separate <button> to push colors to the state or actually doing the RGB to HEX conversion:
const App = () => {
const [state, setState] = React.useState({
value: '',
hex: '',
colors: [],
});
const handleInputChange = React.useCallback((e) => {
const nextValue = e.target.value || '';
const nextHex = nextValue
.trim()
.replace(/[^0-9,]/g, '')
.split(',')
.map(x => `0${ parseInt(x).toString(16) }`.slice(-2))
.join('')
.toUpperCase();
setState((prevState) => ({
value: nextValue,
hex: nextHex,
colors: prevState.colors,
}));
}, []);
const handleButtonClick = React.useCallback(() => {
setState((prevState) => {
return prevState.hex.length === 3 || prevState.hex.length === 6 ? {
value: '',
hex: '',
colors: [...prevState.colors, prevState.hex],
} : prevState;
});
}, []);
return (
<div className="app">
<h1 className="title">Color Palette Generator</h1>
<input
type="text"
className="input"
value={ state.value }
onChange={ handleInputChange } />
<input
type="text"
className="input"
value={ `#${ state.hex }` }
readOnly />
<button className="button" onClick={ handleButtonClick }>
Add color
</button>
<pre className="input">{ state.colors.join('\n') }</pre>
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#app'));
body {
font-family: monospace;
margin: 0;
}
.app {
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.title {
display: flex;
margin: 32px 0;
}
.input,
.button {
margin: 8px 0 0;
padding: 8px;
border: 2px solid black;
background: transparent;
border-radius: 2px;
width: 50vw;
box-sizing: border-box;
}
.button {
cursor: pointer;
}
<script src="https://unpkg.com/react#16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>

React: can I change a state (useState) without causing a repaint, so that I can see a css transition?

I have different "cards" that on click onClick I want their margin-left property to be modified
To do that I use useState, for which I have only one state that is an object that stores the states for all the cards
The below example code shows the problem, but a simplified version that doesn't have a component <Type> and that uses a simple elements array works as expected
So, if I need to use a structure like the one below, how could I keep the transition effect?
Example code
https://codesandbox.io/s/keen-shadow-2v16s?fontsize=14&hidenavigation=1&theme=dark
import React, { useState } from "react";
import styled from "#emotion/styled";
export default function App() {
const [userTap, setUserTap] = useState({});
const elements1 = [...Array(5)];
const elements2 = [...Array(3)];
const Type = ({ list }) =>
list.map((el, i) => (
<Ingredient
key={"draggable" + i}
onClick={e => {
e.stopPropagation();
e.preventDefault();
userTap[i] = userTap[i] ? 0 : 1;
setUserTap({ ...userTap }); // create a new ref to provoke the rerender
return;
}}
userTap={userTap[i]}
>
<div>item</div>
</Ingredient>
));
return (
<>
<Type list={elements1} />
<Type list={elements2} />
</>
);
}
const Ingredient = styled.li`
list-style: none;
cursor: pointer;
margin: 5px;
padding: 5px;
background: #ccc;
border-radius: 3px;
width: 50px;
margin-left: ${props => (props.userTap ? "100px" : "15px")};
transition: all 0.2s ease-in;
`;
The only thing needed to be done, as #larz suggested in the comments, is to move the useState to the last component, as shown below
https://codesandbox.io/s/affectionate-hawking-5p81d?fontsize=14&hidenavigation=1&theme=dark
import React, { useState } from "react";
import styled from "#emotion/styled";
export default function App() {
const elements1 = [...Array(5)];
const elements2 = [...Array(3)];
const Type = ({ list, type }) => {
const [userTap, setUserTap] = useState({});
return list.map((el, i) => (
<Ingredient
key={"draggable" + i}
onClick={e => {
e.stopPropagation();
e.preventDefault();
userTap[type + i] = userTap[type + i] ? 0 : 1;
setUserTap({ ...userTap }); // create a new ref to provoke the rerender
return;
}}
userTap={userTap[type + i]}
>
<div>item</div>
</Ingredient>
));
};
return (
<>
<Type list={elements1} type="one" />
<Type list={elements2} type="two" />
</>
);
}
const Ingredient = styled.li`
list-style: none;
cursor: pointer;
margin: 5px;
padding: 5px;
background: #ccc;
border-radius: 3px;
width: 50px;
margin-left: ${props => (props.userTap ? "100px" : "15px")};
transition: all 0.2s ease-in;
`;

Categories