I'm doing a simple filter on my data using the Semantic UI React 'Input' Component. The data loads first, and then i apply the search filter to narrow down the data based on the user input.
It works, but when typing, it's very slow. I saw another user with a similar issue and someone mentioned onBlur but that doesn't seem to be working for me. Here is my Search component, which is passing a search state back to one of my other components which updates the data. Anyone have any recommendations here?
import React from "react";
import { connect } from "react-redux";
import { Input } from "semantic-ui-react";
import { searchChange, clearSearch } from "../reducers/searchReducer";
const Search = props => {
const handleSearch = event => {
props.searchChange(event.target.value.toLowerCase());
};
const style = {
marginBottom: 10
};
return (
<div style={style}>
<Input
icon="search"
placeholder="Enter filter..."
onChange={handleSearch}
/>
</div>
);
};
const mapStateToProps = state => {
return {
search: state.search,
filter: state.filter
};
};
export default connect(mapStateToProps, {
searchChange,
// filterChange,
clearSearch
})(Search);
Related
I am trying to replicate the search and masonry grid from the official documentation using their Codesandbox example.
However, when I am trying to use it, the code is not returning any gifs back.
I have verified that the JS fetch is indeed returning back gifs, it seems that the grid is not calling the fetchGifs function.
I am hitting the same issue with Carousel component as well.
Can anyone help me with this issue?
Codesandbox link for my implementation - https://codesandbox.io/s/cocky-waterfall-ny9rzk
Component i was trying to use - Search and Grid from https://github.com/Giphy/giphy-js/tree/master/packages/react-components
import { GiphyFetch } from "#giphy/js-fetch-api";
import { Grid } from "#giphy/react-components";
import useDebounce from "react-use/lib/useDebounce";
import React, { useState } from "react";
export default function App() {
const giphyFetch = new GiphyFetch("PZpYG85wQpugMlEx02GGqeKfKZ8eMdFZ");
const [debouncedInput, setDebouncedInput] = useState<string>("");
const [term, setTerm] = useState<string>("");
useDebounce(() => setTerm(debouncedInput), 500, [debouncedInput]);
const NoResults = <div className="no-results">No Results for {term}</div>;
const fetchGifs = (offset: number) => {
return giphyFetch.search(term, { offset, limit: 10 });
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<input
placeholder="type to search"
onChange={({ target: { value } }) => setDebouncedInput(value)}
value={debouncedInput}
/>
<Grid
key={term}
columns={3}
gutter={6}
noResultsMessage={NoResults}
width={400}
fetchGifs={fetchGifs}
/>
</div>
);
}
Try removing strict mode here:
https://codesandbox.io/s/cocky-waterfall-ny9rzk?file=/src/index.tsx
I guess the Grid is incompatible with React 18, possibly due to the useEffect change it brings.
Alternatively you could use React 17.x
Updated looks like they've introduced a fix
https://github.com/Giphy/giphy-js/commit/dade2aa10442c9ca8e6741125071dc1053e89181
//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.
I have semi-legacy code. There's some Formik components that are being wrapped with withFormik().
The primary thing is, the variable/data is not part of the initial props of the wrapped component. A possible example is if React Context API is being used. That means it isn't part of the props. Quick simple React component and withFormik() below with React's useContext hook being used in the wrapped component, but would like access to it in the withFormik() HOC.
Also, the data can't be passed through [hidden] fields in the form. I realize that is an easy way to do it and grab it in values. Wondering if there is another way.
import React, { useContext } from 'react';
import { Form, Field, withFormik } from 'formik';
const MyForm = props => {
const { section } = useContext( SectionContext );
return (
<Form>
<Field
component="input"
name="name"
/>
{errors.name && touched.name && <div id="feedback">{errors.name}</div>}
<button type="submit">Submit</button>
</form>
);
};
const MyEnhancedForm = withFormik({
mapPropsToValues: () => ({ name: '' }),
handleSubmit: (values, { setSubmitting, props }) => {
// Want to use the context, section.
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}
})(MyForm);
Thanks for any help
I'm not understanding some ReactJs behavior and would need some help.
I have a Root Functional Component ("Index"), that contains another functional Component ("Preview").
That Preview component contains several other Functional Components ("InlineField").
The app is a simple form, where InlineField is component that renders an input and also contains a state to know if the field is "opened" or "closed" (when close it is displayed as a text, when open it is displayed as an input).
The global state is defined using hooks ad the "Index" level and moved down to the field through props (I've tried the same using Context). This state contains all form values.
The InlineField Component uses hook to maintain its local state only (is open / is closed).
When a an input is changed it updates the state (Index level) which triggers a re-render of the Index as well as its children.
This translate into the currently edited field (InlineField Component with local state = open) to refresh and lose its state value.
My question:
How can I make sure these InlineField Components retain their state even after updating global state?
I could simply move that InlineField Component state to the global state too, but I don't think it makes much sense.
I must be getting something wrong...
Thanks!
Edit: added code sample
Index Component:
import React, { useState, useEffect } from "react"
import Layout from "../components/layout"
const IndexPage = () => {
const [formValues, setFormValues] = useState({
name: 'Myname',
email: 'myemail#mail.com',
})
const onFormValueChange = (key, value) => {
setFormValues({...formValues, [key]: value})
}
return (
<Layout>
<Preview
key="previewyaknow"
formValues={formValues}
onFieldChange={setFormValues}
/>
</Layout>
)
}
export default IndexPage
Preview Component:
import React from 'react'
import { Box, TextField } from "#material-ui/core"
import { InlineField } from './inlineField'
export const Preview = ({formValues, onFieldChange}) => {
return (
<>
<Box display="flex" alignItems="center">
<InlineField
value={formValues.email}
onChange={onFormValueChange}
id="email"
field={<TextField value={formValues.email}/>>>}
/>
</>
)
}
InlineEdit Component
import React, { useState, useEffect } from "react"
export const InlineField = ({onChange, value, id, field}) => {
const [isEdit, setIsEdit] = useState(false)
const onBlur = (e) => {
setIsEdit(false)
}
let view = (<div>{value}</div>);
if (isEdit) {
view = (
<FieldContainer className={classes.fieldContainer}>
{React.cloneElement(field, {
'onBlur': onBlur,
'autoFocus': true,
'onChange': (e) => {
onChange(id, e.target.value)
}
})
}
</FieldContainer>
)
}
return (
<div onClick={()=>setIsEdit(!isEdit)}>
{view}
</div>
)
}
I'm having an issue where react-loadable is causing one of my input components to re-render and lose focus after a state update. I've done some digging and I can't find anyone else having this issue, so I think that I'm missing something here.
I am attempting to use react-loadable to dynamically include components into my app based on a theme that the user has selected. This is working fine.
./components/App
import React from 'react';
import Loadable from 'react-loadable';
/**
* Import Containers
*/
import AdminBar from '../../containers/AdminBar';
import AdminPanel from '../../components/AdminPanel';
import 'bootstrap/dist/css/bootstrap.css';
import './styles.css';
const App = ({ isAdmin, inEditMode, theme }) => {
const MainContent = Loadable({
loader: () => import('../../themes/' + theme.name + '/components/MainContent'),
loading: () => (<div>Loading...</div>)
});
const Header = Loadable({
loader: () => import('../../themes/' + theme.name + '/components/Header'),
loading: () => (<div>Loading...</div>)
});
return (
<div>
{
(isAdmin) ? <AdminBar
className='admin-bar'
inEditMode={inEditMode} /> : ''
}
<Header
themeSettings={theme.settings.Header} />
<div className='container-fluid'>
<div className='row'>
{
(isAdmin && inEditMode) ? <AdminPanel
className='admin-panel'
theme={theme} /> : ''
}
<MainContent
inEditMode={inEditMode} />
</div>
</div>
</div>
);
};
export default App;
./components/AdminPanel
import React from 'react';
import Loadable from 'react-loadable';
import './styles.css';
const AdminPanel = ({ theme }) => {
const ThemedSideBar = Loadable({
loader: () => import('../../themes/' + theme.name + '/components/SideBar'),
loading: () => null
});
return (
<div className='col-sm-3 col-md-2 sidebar'>
<ThemedSideBar
settings={theme.settings} />
</div>
);
};
export default AdminPanel;
This is what my <ThemedSideBar /> components looks like:
./themes/Default/components/SideBar
import React from 'react';
import ThemeSettingPanel from '../../../../components/ThemeSettingPanel';
import ThemeSetting from '../../../../containers/ThemeSetting';
import './styles.css';
const SideBar = ({ settings }) => {
return (
<ThemeSettingPanel
name='Header'>
<ThemeSetting
name='Background Color'
setting={settings.Header}
type='text'
parent='Header' />
<ThemeSetting
name='Height'
setting={settings.Header}
type='text'
parent='Header' />
</ThemeSettingPanel>
);
};
export default SideBar;
./components/ThemeSettingPanel
import React from 'react';
import { PanelGroup, Panel } from 'react-bootstrap';
const ThemeSettingPanel = ({ name, children }) => {
return (
<PanelGroup accordion id='sidebar-accordion-panelGroup'>
<Panel>
<Panel.Heading>
<Panel.Title toggle>{name}</Panel.Title>
</Panel.Heading>
<Panel.Body collapsible>
{children}
</Panel.Body>
</Panel>
</PanelGroup>
);
};
export default ThemeSettingPanel;
./containers/ThemeSetting
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { themeSettingChange } from '../App/actions';
import ThemeSetting from '../../components/ThemeSetting';
class ThemeSettingContainer extends Component {
constructor(props) {
super(props);
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange(name, parent, value) {
const payload = {
name: name,
parent,
value: value
};
this.props.themeSettingChange(payload);
}
render() {
return (
<ThemeSetting
name={this.props.name}
setting={this.props.setting}
parent={this.props.parent}
type={this.props.type}
handleOnChange={this.handleOnChange} />
);
}
}
//----Redux Mappings----//
const mapStateToProps = (state) => ({
});
const mapDispatchToProps = {
themeSettingChange: (value) => themeSettingChange(value)
};
export default connect(mapStateToProps, mapDispatchToProps)(ThemeSettingContainer);
./component/ThemeSetting
import React from 'react';
import TextField from '../common/TextField';
import './styles.css';
const ThemeSetting = ({ name, setting, type, parent, handleOnChange }) => {
return (
<div className='row theme-setting'>
<div className='col-xs-7'>
{name}
</div>
<div className='col-xs-5'>
{
generateField(type, setting, name, parent, handleOnChange)
}
</div>
</div>
);
};
function generateField(type, setting, name, parent, handleOnChange) {
const value = setting ? setting[name] : '';
switch (type) {
case 'text':
return <TextField
value={value}
name={name}
parent={parent}
handleOnChange={handleOnChange} />;
default:
break;
}
}
export default ThemeSetting;
./components/common/TextField
import React from 'react';
import { FormControl } from 'react-bootstrap';
const TextField = ({ value, name, parent, handleOnChange }) => {
return (
<FormControl
type='text'
value={value}
onChange={(e) => {
handleOnChange(name, parent, e.target.value);
}} />
);
};
export default TextField;
When a field inside of my Admin Panel is updated, a state change is triggered. It seems like this triggers react-loadable to re-render my <ThemedSideBar /> components which destroys my input and creates a new one with the updated value. Has anyone else had this issue? Is there a way to stop react-loadable from re-rendering?
EDIT: Here is the requested link to the repo.
EDIT: As per conversation in the comments, my apologies, I misread the question. Answer here is updated (original answer below updated answer)
Updated answer
From looking at the react-loadable docs, it appears that the Loadable HOC is intended to be called outside of a render method. In your case, you are loading ThemedSideBar in the render method of AdminPanel. I suspect that the change in your TextEdit's input, passed to update your Redux state, and then passed back through the chain of components was causing React to consider re-rendering AdminPanel. Because your call to Loadable was inside the render method (i.e. AdminPanel is a presentational component), react-loadable was presenting a brand new loaded component every time React hit that code path. Thus, React thinks it needs to destroy the prior component to appropriately bring the components up to date with the new props.
This works:
import React from 'react';
import Loadable from 'react-loadable';
import './styles.css';
const ThemedSideBar = Loadable({
loader: () => import('../../themes/Default/components/SideBar'),
loading: () => null
});
const AdminPanel = ({ theme }) => {
return (
<div className='col-sm-3 col-md-2 sidebar'>
<ThemedSideBar
settings={theme.settings} />
</div>
);
};
export default AdminPanel;
Original answer
It seems that your problem is likely related to the way you've built TextField and not react-loadable.
The FormControl is taking value={value} and the onChange handler as props. This means you've indicated it is a controlled (as opposed to uncontrolled) component.
If you want the field to take on an updated value when the user types input, you need to propagate the change caught by your onChange handler and make sure it gets fed back to the value in the value={value} prop.
Right now, it looks like value will always be equal to theme.settings.Height or the like (which is presumably null/empty).
An alternative would be to make that FormControl an uncontrolled component, but I'm guessing you don't want to do that.