Prefill an editable input field with data from API response in React - reactjs

I have a function that I use to make the API call and fetch the response. There is also another react file that renders the UI page. I'm able to store the response from the API using props and state. The issue I'm facing is that I'm able to prefill the input field with the correct data, but I'm unable to edit the input field. Also, I have to pass the input to another component.
Below is my code:
export const SelectorForm = ({ selectorDetail, selectorId, otherProp }) => {
return (
<>
<Row>
<input type="text" value={selectorDetail} onChange={updateSelectorIdAction} />
</Row>
</>
);
};
export const mapStateToProps = state => {
return {
selectorDetail: state.selectorDetail,
selectorId: selectorIdSelector(state),
};
};
export const mapDispatchToProps = {
updateSelectorIdAction: updateSelectorId,
};
export default SelectorForm;
export const updateSelectorId = (value) => ({
type: UPDATE_SELECTOR_ID,
payload: value,
});
What I need is to display the selectorDetail in the input field, take user input in selectorId and pass selectorId to another component (this part is done).
How should I change my onChange to do this?

Related

React and redux not updating component until second re-render

I have a react component written in typescript that has an input field and a button. The user can type an input and after pressing the button, a table will be populated with the relevant results. I use React.useEffect() to only run the search code if the searchTerm has changed. Running the code inside useEffect() will populate the table rows which then get stored for display in the able component.
export const SearchWindow: React.FC<Props> = props => {
const [searchInput, setSearchInput] = React.useState(''); // text value inside input box
const [searchTerm, setSearchTerm] = React.useState(''); // term to search for (set once search clicked)
const handleSearchInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchInput(event.target.value);
};
React.useEffect(() => {
if (searchTerm.length > 0) {
getSearchResults(props.config, searchTerm).then(async searchResults => {
const rows = searchResults.map(result => {
return {
cells: {
text: result.text,
},
};
});
store.dispatch(setSearchResults(rows));
});
}
}, [searchTerm]); // Only run search if searchTerm changes
// on 1st search, prints blank until another character is typed
console.log(store.getState().documentationIndex.searchResults);
return (
<form>
<input
type='search'
placeholder='Enter Search Term'
onChange={handleSearchInputChange}
value={searchInput}
/>
<<button onClick={() => setSearchTerm(searchInput)}>Search</button>
</form>
<Table
...
...
rows={store.getState().documentationIndex.searchResults}
/>
);
};
// -------------- store code ----------------------------
import { createSlice, PayloadAction } from '#reduxjs/toolkit';
// Reducer
export interface DocumentationIndexState {
searchResults: DataTableRow<TableSchema>[];
}
const initialState: DocumentationIndexState = {
searchResults: [],
};
const store = createSlice({
name: 'documentationIndex',
initialState,
reducers: {
setSearchResults: (state, action: PayloadAction<DataTableRow<TableSchema>[]>) => {
state.searchResults = action.payload;
},
},
});
export default store.reducer;
export const {
setSearchResults,
} = store.actions;
The code behaves as expected except for on the first search. Take the following sequence:
User inputs 'hello' into search input box and clicks 'Search' button.
Search is run successfully but nothing is displayed in the Table component.
User types any random character following 'hello' which already exists in the input box.
Table component is updated with the search results for 'hello' successfully.
User deletes 'hello' from input box and types in 'foobar' then presses 'Search' button.
Search results for 'foobar' are displayed correctly
When I print
console.log(store.getState().documentationIndex.searchResults);
right before the return(.... that renders the component, the results are blank after the first search. When I type one more character, the results populate.
I'm at the end of my wits as to why this is happening so any help would be appreciated!
Never use store.getState in a React component. Use useSelector
const searchResults = useSelector(state => state.documentationIndex.searchResults)
Otherwise your component will not update when the state updates.

Sending event.target.value as parameter to dispatched action

I'm trying to update my store's "searchField" value (it starts as a blank string) when a user inputs a value into the text box of the free-response component. When I type in the field, the "searchField" property becomes undefined and I fear it's a fundamental error I can't see (I'm still quite new to Redux.) I've included my reducer, component and relevant action code below. Any help is greatly appreciated!
free-response.component.jsx:
export const FreeResponse = ({searchField,changeSearchField,i}) =>{
let questionURL="/images/question";
return(
<div className="main">
<img src={`${questionURL}${i}.png`}/>
<form >
<input type="text" onChange={changeSearchField} value={changeSearchField} alt="text field"/>
</form>
</div>
)}
const mapStateToProps=state=>({
searchField: state.question.searchField,
i:state.question.i
})
const mapDispatchToProps=dispatch=>({
changeSearchField: (e)=>dispatch(changeSearchField(e.target.value))
})
export default connect(mapStateToProps,mapDispatchToProps)(FreeResponse);
question.reducer.js:
return{
...state,
searchField:changeSearchField(e)
};
question.utils.js (action creator):
export const changeSearchField=(e)=> e;
question.actions.js:
export const changeSearchField=()=>({
type:QuestionActionTypes.CHANGE_SEARCHFIELD,
})
It seems like you did not define the payload for your changeSearchField action. This is to ensure that the values from your form input will be passed on by the action creator.
This is one way you can do it:
export const changeSearchField = (searchField) => ({
type: QuestionActionTypes.CHANGE_SEARCHFIELD,
payload: searchField
});
And on your reducer, you just need to update the store with the values from the payload (the below may differ depending on the actual structure of your store):
return {
...state,
searchField: action.payload.searchField,
};

ReactJS Context: Passing array through context

I currently have a project and I need to pass the array to another component for my search bar. I am using React's Context.
I've tried passing my data but I seem to get an undefined value in my console.
Code in Context.js
Export const SearchContext = createContext();
This is the code in MainPage.js:
const data = [json_1, json_2];
const array = data.map(values => {
const search = _.flattenDeep(values);
values.search = search;
return values;
})
<SearchContext.Provider value={array} />
and in my Searchbar.js
const options = useContext(SearchContext);
console.log(options);
<AutoComplete
className="searchbar"
placeholder="Search..."
dataSource = {options}
onfilterOption={(inputValue, option) =>
option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
}
/>
In the console.log I get an undefined value. This data should also be able to populate my search bar.
please look at this Gist : https://gist.github.com/nimahkh/9c008aaf2fd2d1cc83cd98c61e54979a
you have to wrap your component with Provider and inside of that component that is Wrapped , you have access to value , it's impossible to fetch value , out of the box

reduxForm: how to best dispatch an action?

I am trying to submit an address form using redux form. It seems like a good way of handling the input data and validation.
I am just wondering if I can make the syntax a bit cleaner, because, frankly, trying to use connect at the same time makes the code a mess at the bottom. In my case, I want to send the address data to a Node endpoint, so I need to call an action generator which sends an AJAX request. I'm wondering if I'm missing something obvious to make dispatching an action inside the submit function easier.
class AddressForm extends Component {
renderContent() {
return formFields.map(({ name, label }) => (
<Field
key={name}
name={name}
label={label}
type='text'
component={FormField}
/>
)
);
};
render() {
return (
<form onSubmit={this.props.handleSubmit}>
{this.renderContent()}
<button type="submit">Next</button>
</form>
);
};
};
const validate = (values) => {
const errors = {};
errors.email = validateEmail(values.email || '');
formFields.forEach(({ name }) => {
if (!values[name]) errors[name] = 'Please provide a value';
});
return errors;
};
const myReduxForm = reduxForm({
validate,
form: 'addressForm'
})(AddressForm);
const mapDispatchToProps = (dispatch, ownProps) => ({
onSubmit: data => dispatch(submitForm(data, ownProps.history))
});
export default connect(null, mapDispatchToProps)
(withRouter(myReduxForm));
Sure, instead of connecting, you can use the handleSubmit prop inside your component. It allows you to supply a callback with three arguments: values, dispatch and props. So you can do something like:
<form onSubmit={this.handleSubmit((values,dispatch,{submitForm})=> dispatch(submitForm(values)))} />

ReduxForm: dispatching 'change' action inside form in FormSection

I would like to dispatch a change action in a FormSection.
Currently I'm doing this in myForm.jsx:
<FormSection name="content">
<MyFormSection sectionName="content" formName="PanelForm"/>
</FormSection>
following this in MyFormSection.jsx
import { change } from 'redux-form';
// my component...
const mapDispatchToProps = (dispatch, props) => ({
change: (name, value) => dispatch(change(props.formName, props.sectionName+"."+name, value)),
})
I don't like to pass down the form both the name of the form and the section.
Would anybody have a better way of doing this?
If you are inside a Redux-Form Input component you can use this.props.input.onChange(value)
but if you are just wanting to create a function that when executed changes that value of a form which is not used in a Redux-Form Field Component, I think you are doing it the correct way. Here is another example I did just so you can see. This function was being passed down to a child form through props for one very specific way of changing values in my 'wizard' form.
const mapDispatchToProps = (dispatch) => {
return {
changeFormAction: (fieldName, value): void => {
dispatch({
type: '##redux-form/CHANGE',
meta: {
form: 'wizard',
field: `styles.[${fieldName}]`,
touch: false,
persistentSubmitErrors: false
},
payload: value
})
}
}
}

Resources