0

我正在使用 react with redux,为执行以下操作的操作实现 redux-thunk:

  1. 电话axios.get()
  2. 第一then()条语句映射到 JSON 以将键和值转换为前端规范
  3. 第二then()条语句遍历 JSON 上的一个属性(0 - 5 项)axios.get(),为每个属性创建一个调用数组,封装在 Promise 中。这个数组被传递给一个async/wait函数,确保这些承诺在继续之前都得到解决。
  4. 第三then()条语句再次遍历列表,删除任何未定义的值,然后成功触发动作创建者。在 Chrome 开发工具上记录数据可以验证所有数据都存在,并且没有未解决的承诺。
  5. 在 Chrome 开发工具中将此有效负载从 reducer 记录到映射数据的容器中,会导致相同的问题。数据存在于控制台中,但无法在任何地方访问。

我将在下面提供有效的 js,尽管它在此代码段中不起作用:

/* - - - - - - Actions - - - - - - -

Using thunk, promises, and async/await to insure data is present before sending to reducer 

*/

export function getSingleMessage(id, callback) {
  return function (dispatch) {
    const request = axios.get(`${ROOT_URL}getDialogue.php?message_id=${id}`)
    .then((resp) => {
      // Just reformatting JSON
      const { data } = resp;
      console.log('message: ', data)
      let formValues = {};
      let messageIds = [];

      formValues.dialogue_query = data.message_query;
      formValues.dialogue_text = data.message;
      formValues.message_id = data.messages_id;
      if (data.attachment) {
        formValues.attachment_text = data.attachment.text;
        formValues.attachment_fallback = data.attachment.fallback;
        formValues.image_url = data.attachment.image_url;
        if (data.attachment.actions) {
          /* 
            1) these buttons need unique id's
            2) the last unique id needs to not collide with any other id's of new buttons

            // Map through actions
              if key is 'name'
                button_text
              if key is 'type'
                action
          */
          
          let key = 0;
          

          formValues.buttons = data.attachment.actions.map( action => {
            let newAction = {};
            
            Object.keys(action).forEach( key => {
              let curr = action[key];
              let newCurrArr;
              let newCurr;
              if (key === 'name') {
                newAction.button_text = curr;
              }
              
              if (key === 'value') {
                if (curr.includes('Value[')) {
                  newCurrArr = curr.split('=');
                  newCurr = newCurrArr.pop();
                  newAction.button_action = newCurr;
                  curr = newCurrArr.join('=')
                  console.log('CURRRRRRR: ', curr)
                }
                if (curr.includes('message_id=')) {
                  newCurrArr = curr.split('=').pop();
                  console.log('NEWCURRARR: ', newCurrArr)
                  let newNewCurrArr = newCurrArr.split('&');
                  console.log('NEWNEWCURRARR: ', newNewCurrArr)
                  newCurr = newNewCurrArr.shift();
                  messageIds.push(newCurr);
                  newAction.message_id = Number(newCurr);
                  
                }
              }
            });

            newAction.id = key;
            key++;
            return newAction;
          })
        }
      }
      return [formValues, messageIds]
    })
    .then((resp) => {
    // creating array of promises, and resolving with async/await
      const formValues = resp[0];
      const messageIds = resp[1];

      const promises = messageIds.map((id) => axios.get(`${ROOT_URL}getDialogue.php?message_id=${id}`));
      console.log('PROMISES!!!!!! ', promises)

      if (formValues.buttons) {
        async function getPreviewMessages() {
          let resolvedPreviewMessages = await Promise.all(promises);
          return formValues.buttons.map((button, i) => {
          // setting previewMessages to buttons
            button.previewMessage = resolvedPreviewMessages[i];
            return button;
          })
        }
        getPreviewMessages();
      } 
      return formValues;
    })
    .then((formValues) => {
      // 
      console.log('RESP: ', formValues)
      // cleans up any null or undefined values
      
      for (let key in formValues) {
        if (formValues[key] === 'null' || formValues[key] === 'undefined') {
          formValues[key] = '';
          console.log(`formValues[${key}] = ${formValues[key]}`);
        }
        console.log(`formValues[${key}] = ${formValues[key]}`);
      }
      console.log('formValues: ', formValues)

      dispatch(fireOffSingleMessage({data: formValues}));
    })
    .catch((err) => {
      console.log(err)
      return err;
    })
    console.log('requesssssssst: ', request)
    callback();
    
  }
}

function fireOffSingleMessage(request) {
  // data is all resolved in Chrome Dev Tools
   console.log('FIRED OFF SINGLE MESSAGE~~~~~~', request)
  return {
    type: GET_SINGLE_MESSAGE,
    payload: request,
  }
}

// - - - - - - Reducer - - - - - - -

import { GET_SINGLE_MESSAGE, REFRESH_SINGLE_MESSAGE, ADD_BUTTON_EDIT, EDIT_BUTTON_EDIT, DELETE_BUTTON_EDIT } from '../constants';

export default (state = {}, action) => {
	switch(action.type) {
		case GET_SINGLE_MESSAGE: 
		console.log('Data is present!: ', action.payload.data.buttons)
			return action.payload;
		case REFRESH_SINGLE_MESSAGE:
			return {}
      
    // Not involved
		case ADD_BUTTON_EDIT:
			console.log('ADD_BUTTON_EDIT reducer fired off!', state);
			if (Array.isArray(state.data.buttons) && state.data.buttons.length > 0) {
			  return {
			    ...state, 
			    data: {... state.data, buttons: [...state.data.buttons, action.button]}
			  };  
			} else {
			  return {
			    ...state, 
			    data: {... state.data, buttons: [action.button]}
			  }; 
			}
		case EDIT_BUTTON_EDIT:
		console.log('EDIT_BUTTON_EDIT reducer fired off!', state);
			const newStateEdit = state.data.buttons.map(button => {
			  if (button.id === action.button.id) {
			  	console.log('button.id: ', button.id)
			  	console.log('action.button.id: ', action.button.id)
			    return action.button;
			  } else {
			    return button;
			  }
			})
			return {
			  ...state, 
			  data: {... state.data, buttons: newStateEdit}
			}; 
		case DELETE_BUTTON_EDIT:
		console.log('DELETE_BUTTON_EDIT reducer fired off!', state);
			const newStateDelete = state.data.buttons.filter(button => button.id !== action.target);
			return {
			  ...state, 
			  data: {... state.data, buttons: newStateDelete}
			}; 
		default:
			return state;
	}
}

// - - - - Root Reducer - - - - -

import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';

import getAllDialogs from './getAllDialogs-reducer.js';
import handleMessages from './messages-reducer.js';
import handleMessage from './message-reducer.js'
import handleMessageId from './messageId-reducer.js'
import updateButtons from './buttons-reducer.js';
import renderButtonForm from './buttonForm-reducer.js';
import handleId from './buttonId-reducer.js';
import handleNewMessageQuery from './newMessageQuery-reducer.js';
import handleMessageQueries from './messageQueries-reducer.js';
import handleButton from './button-reducer.js';
import handleReRenderToggle from './reRenderToggle-reducer.js';
import handleContext from './context-reducer.js';

const rootReducer = combineReducers({
  dialogs: getAllDialogs,
  messages: handleMessages,
  message: handleMessage,
  messageId: handleMessageId,
  buttons: updateButtons,
  buttonForm: renderButtonForm,
  button: handleButton,
  buttonId: handleId,
  reRender: handleReRenderToggle,
  newMessageQuery: handleNewMessageQuery,
  messageQueries: handleMessageQueries,
  context: handleContext,
  form: formReducer,
});

export default rootReducer;

// - - - - - container - - - - - 


  renderFakeButtons(buttons = []) {
    const _that = this;
    buttons.forEach((button, i) => {console.log(button)});
    const { history, deleteButton, renderButtonForm, reRenderToggle, getSingleMessage, getSinglePreviewMessage } = this.props;
    // DATA LOGS IN DEV TOOLS!!!!
    console.log('BUTTONS: ', buttons)
    return buttons.map((button) => {
      // DATA LOGS IN DEV TOOLS!!!
      console.log('BUTTON: ', button)
      return (
        <div 
          className="top-margin"
          key={button.id}>
          <div className="row">
            <Card>
            <CardHeader
              title={`Button Text: ${button.button_text}`}
              subtitle={`Button Action: ${button.button_action}`}
              actAsExpander={true}
              showExpandableButton={true}
            />
            <CardText expandable={true}>
              <div>
{/*THIS IS WHERE I WOULD ACCESS THE PROPERTY EXPLICITLY*/}
                {JSON.stringify(button)}
              </div>
            </CardText>
            <CardActions>
              <FlatButton 
                label={`Next Message: ${button.message_id}`} 
                onTouchTap={function(){
                    getSingleMessage(button.message_id, () => {
                      history.push(`/message/edit/${button.message_id}`);
                    })
                  }
                }
              />
              <FlatButton 
                label="Delete Button"
                onTouchTap={() => { deleteButton(button.id, 'edit'); reRenderToggle(); }} 
              />
            </CardActions>
            </Card>
          </div>

        </div>
      )}
    );
  }
  
  render() {

    const _that = this;
    const { state, onSubmit, onTest, onBack, renderMessageQuerySelect, renderInputFields, renderTextAreaFields, injectButtonForm, renderUserTestSelectOptions } = this;
    const { messages, getMessageId, messageId, handleSubmit, renderButtonForm, buttons, reset, buttonForm, refreshSingleMessage, toggleMessageQueryField, newMessageQuery, initialValues, addButtons } = this.props;

    let titleName;

    if (messages.messages && messages.messages.dialog_name) {
      titleName = messages.messages.dialog_name;
    } else if (messages.messages && messages.messages.messageQuery) {
      titleName = messages.messages.messageQuery
    }

    return (
      <div>
        <MuiThemeProvider>

          <div className="row">
            <div className="col-md-6">
              <form onSubmit={handleSubmit(onSubmit.bind(this))}>
                <div className="form-group">
                <h4> Edit Message {messageId} from "{titleName}" </h4>
                  <Field
                    label="Message Query"
                    name="dialogue_query"
                    component={newMessageQuery ? renderInputFields : renderMessageQuerySelect.bind(this)}
                  />
                  New
                  <input  
                    type="checkbox"
                    onClick={toggleMessageQueryField}
                  />
                </div>
                <Field
                  label="Text of Message"
                  name="dialogue_text"
                  component={renderTextAreaFields}
                />
                <Field
                  label="Attachment Text"
                  name="attachment_text"
                  component={renderInputFields}
                />
                { state.error !== null ? <div className="form-group has-danger"><div className="text-help">{state.error}</div></div> : null }
                <Field
                  label="Attachment Fallback"
                  name="attachment_fallback"
                  component={renderInputFields}
                />
                { state.error !== null ? <div className="form-group has-danger"><div className="text-help">{state.error}</div></div> : null }
                <Field
                  label="Image Url"
                  name="image_url"
                  component={renderInputFields}
                />
                <div className="form-group">
                  <div>
                    <div
                      className="btn btn-primary"
                      onClick={function() {
                        renderButtonForm(); 
                      }}
                    >
                      Click to Add Buttons
                    </div>
                    
                    <div>
                    { initialValues && initialValues.buttons ? _that.renderFakeButtons(initialValues.buttons) : '' }
                    </div>

                  </div>
                </div>

                <Field
                  label="Test Primary User"
                  name="primary_test_user_id"
                  component={renderUserTestSelectOptions}
                />
                <Field
                  label="Test Secondary User"
                  name="secondary_test_user_id"
                  component={renderUserTestSelectOptions}
                />
                <button 
                  type="submit" 
                  className="btn btn-primary"
                >
                  Submit
                </button>
                <button 
                  type="submit" 
                  className="btn btn-primary buttons-margin"
                  onClick={handleSubmit(onTest.bind(this))}
                >
                  Test
                </button>
                <div className="btn btn-primary buttons-margin" onClick={reset}>Reset to Original</div>
                <div className="btn btn-danger buttons-margin" onClick={onBack.bind(this)}>Back</div>
              </form>
            </div>
            <div className="col-md-5">
              <div className="row">
                <div className="col-md-12">
                  {injectButtonForm(buttonForm)}
                </div>
              </div>
              <div className="row">
                <div className="col-md-12">
                  <div className="bottom-top-margin">



                  </div>
                </div>
              </div>
            </div>
          </div>
        </MuiThemeProvider>
      </div>
    );
  }
}

function validate(values) {
  const errors = {};
  if (!values.dialogue_text) {
    errors.dialogue_text = "Enter message text";
  }

  return errors;
}

MessageEdit = reduxForm({
  validate,
  form: 'MessageEditForm', // a unique name for this form
  enableReinitialize: true,
})(MessageEdit)

MessageEdit = connect(
  state => { 
    const newState = {
    dialogs: state.dialogs,
    messages: state.messages,
    messageId: state.messageId,
    initialValues: state.message.data,
    buttonForm: state.buttonForm.isRendered,
    buttonId: state.buttonId, 
    messageQueries: state.messageQueries,
    reRender: state.reRender,
    newMessageQuery: state.newMessageQuery,
    context: state.context,
  }
  console.log('MessageEdit newState: ', newState);
  return newState },
  { addButtons, postNewMessage, testMessage, getSingleMessage, refreshSingleMessage, deleteMessage, getDialogMessages, refreshButtons, deleteButton, refreshButtonId, refreshButtons, renderButtonForm, unRenderButtonForm, toggleMessageQueryField, getMessageId, getMessageQueries, reRenderToggle }
)(MessageEdit)

export default MessageEdit;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

根据 Chrome 开发工具,为什么我的数据在我的 THUNK 中可用,但实际上无法访问?

4

1 回答 1

0

在您的渲染功能中,您有...

{ initialValues && initialValues.buttons ? _that.renderFakeButtons(initialValues.buttons) : '' }

我假设没有出现。您实际上不需要在initialValues此处作为参数传递,而只需传递所有道具。

尝试做这样的事情:

renderFakeButtons = (props) => {
  if (!props.initialValues.buttons) return;

  // do some rendering here
}

render() {
  const MaybeFakeButtons = this.renderFakeButtons;

  return (
    <div><MaybeFakeButtons {...this.props} /></div>
  )
}

我的猜测是 React 无法识别嵌套更新,因此initialValues树的三元部分正在更新。尝试传播所有道具(如上所示),看看它是否有效。

于 2017-08-23T18:51:37.010 回答